home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1996 #15
/
Monster Media Number 15 (Monster Media)(July 1996).ISO
/
os2
/
srefv112.zip
/
SREFILTR.A80
< prev
next >
Wrap
Text File
|
1996-05-29
|
129KB
|
3,838 lines
/* sre-filter ver 1.11
a HTTP filter prgram, written in REXX,
for use with the GOSERVE internet server
This program was written by Daniel Hellerstein, DANIELH@ECON.AG.GOV, 5/24/96
Feel free to use and distribute this beta test software.
Copyright is retained by the author, but no limits are placed on the
use of any portion of the code found herein.
Disclaimer:
This program, and associated files, is not in any way an official,
semi-official, or unofficial product of my employers (USDA/ERS).
At this writing (5/24/96), it is a beta-test program --
no liability can be assumed for errors in the program,
and NO warranty is hereby issued. USE this at YOUR own risk.
That said, our experience indicates that this is
a stable product. However, should you discover
problems with this software, PLEASE contact
Daniel Hellerstein at the above E-mail address!
(Plese see the READ.ME file for the full disclaimer)
*/
/*******************************************************************************/
/* ------------------------ Description ------------------------------- */
/*
GoServe, an IBM EWS Internet server, requires "filter" programs to properly
respond to HTTP requests. GoServe itself handles the details of communications.
SRE-FILTER (until we come up with a better name) is designed for this role.
SRE-FILTER is meant to provide an easy to configure and maintain Web Server
for small to medium sites. It lacks certain high-end features, but it
provides a number of features designed to ease the creation of sets of web pages.
For a detailed description of how to use this filter, see the SREFILTER.HTM
and SUMMARY.HTM files. You may also want to look at INITFILT.DOC -- which
describes in some detail the user-configurable parameters used by SRE-FILTER.
Lastly, the FILTINST.HTM document provides a front-end to an on-line configuator.
----------------
The following summarizes the steps SRE-FILTER takes when considering a request:
1) Set up default values for user-configurable parameters. Most of these
are reset at step 2.
1a) If necessary, read SREFLIB1.RXL procedure libarary into macrospace.
1b) Parse the request, ip address, etc. (from GoServe)
2) Read INITFILT.80, which contains a number of user-configurable parameters,
and the names of various files and directories needed by SRE-FILTER.
The ambitous programmer could forgo the use of INITFILT.80 by hard coding
these values at step 1, and setting the INITFILT_FILE variable to " ".
Note that INITFILT is "interpreted" by REXX -- if a syntax error occurs,
the remainder of INITFILT.80 is skipped. You should probably use PMPRINTF
to check the status of SRE-FILTER anytime you change INITFILT.
3) Clean up results from steps 1 and 2.
4) Check on, and create if necessary, a few "counter files"
4a) Load "input files" into macrospace (if a change detected)
5) If the requested action is one of the PUBLIC_ACTIONS, then skip all the
logon/access-control stuff. Otherwise..
6) DeterminE if logon is needed.
Check to see if this IP is an owner
Check to see if this is inhouse or in user_file
6a) If ALLOW_ACCESS is binding, see if this request string is
"not accessible" to this client. Note that the original, non-modified,
request string is examined (as sent by the client, prior to
"alias replacement", etc. by this filter)
6b) see if ssi and ssp privs are granted (checking privilege list)
6c)If access is allowed, and RECORD_OPTION=YES or YES_ALL), then
records reciept of this URL (if YES, then minus any stuff ?xxx).
7) Process the request string:
a1) See if empty request. If so, action=Default
(NOTE: Jump here if a PUBLIC_FILES was requested)
a2) If check_alias is on, then see if this action is an alias for some other action
If so, use this other action
Skip to a5 if VERB <> GET
Skip to 10 if a PUBLIC_FILES was requested
a3) Syntax check: is this a "no extension request?" (no ?, and no . in the
final portion of the request string)
If so, append something to the end ?
a4) See if this action is "directory" name (not the root directory though).,
i.e. xxx/yyy/. If so,
check autoname for candidate "directory's default document" names
(note: if ismap_url string, or CGI-BIN appear in the sel, skip
this step)
a5) Process the verb, using the SEL from steps a2-a5.
a5b) If the action is for a document which does not exist (and VERB=GET)
go back to autoname and try again
8) Replace ~ with the HOME_DIR.
9) Maybe ask for privilege info (if privs needed and logon was not required)
10) Do "virtual drive" substitution on the sel.
Among other uses, this allows non data-directory
files to be freely accessible. It also is used to change the location
of server side routines, mappable images, and cgi-bin programs (when
used with an appropriate alias).
11) a) If "special request" (begins with !), do it now.
b) If HEAD. Do a very simple HEAD request. This should be fixed up in the future.
c) If a GET request, determine 1 of 4 classes:
i) 1) Simple file name eg; /ZIPFIP/ZIPFIP.HTM
File is retrived, with possible ssi's
2) File name with OPTIONS, eg; /OVERVIEW.HTM?From+ZIPFIP+main+page
File is retrieved, with possible sss's (some of which may use these OPTIONS
Note: server side includes (ssi's) are accomplished with:
HEADERS and FOOTERS
REPLACE INCLUDE OPTIONS and INTERPRET keyphrases
which are processed recursively
3) Form name with parameters, eg; /CALC/DOCALC?12-34%2B51
sref_getpost is called with appropriate arguments
4) Mappable image (ISMAP) response. URL must include the
ismap_url string (say, MAPIMAGE/).
Eg ISMAPDIR/US/USMAP?41+239
Note: ISINDEX type searchable-index requests are identical in
appearance to case 2. To avoid this problem, use an ALIAS
to a class 3 (i.e.; that requests DOSEARCH)
ii) If case 1 or 2; check for existence of file.
Do server side includes
Return the file (possibly modified)
If accept_range is yes, then maybe only return a byte range.
iii) If case 3 or 4
If case 4, call image map handler (which will do a redirect )
If SENDFILE or GETAFILE, prepare a few variables
Call sref_getpost "server side processing" handler external routine
d) If POST request
Call sref_getpost "server side processing" handler external routine
12) If POST_FILTER=YES, then before returning call the POSTFILT.80
routine. POST_FILTER is ALWAYS called (regardless of whether
successful transaction occurred). Note that POST_FILTER
processing occurs AFTER a "completion code" (after the response
has been sent back to the client). Thus, complicated Post_filter's
will not slow down response time (except to the extent that it
bogs down the computer for the NEXT client!)
------------------
NOTES NOTES NOTES
1) Sre-filter uses a "macrospace library", loaded from SREFLIB1.RXL, to store a
number of useful routines. Their names all begin with SREF_.
For information on these routines, see SREFLIB1.DOC.
Note that the REXXLIB utility library is used (see SREFLIB1.DOC for details
on this defunct shareware)
2) A note on caching:
SRE-FILTER will allow caching when:
CHECKLOG=NO
ALLOW_ACCESS=YES
no server side includes were performed.
If any of these conditions are not meant, then NOCAHCE will be specified
on all file transfers.
3) A note on ports:
We assume here that GoServe is using port 80. If it is not:
you will need to change the names of all SRE-FILTER files that end with
.80 to be the .nnn, where nnn is the port being used by GoServe (nnn
can be > 3 digits if HPFS is being used).
In particular, change the names of SREFILTR.nnn and initfilt.nnn.
It is possible to run multiple instances of the
server, using different ports, different working directories (and
possibly different data directories), and different copies of the
various .IN, .CTL, .CNT, and (most importantly) the .80 (now .nnn) files.
HOWEVER, only one of the "ports" will use the .IN files that are
stored in macro_space. By default, this will be port 80. To change
this, you'll have to "recompile" SREFLIB1.RXL (see SREF1LIB.DOC for
notes on this).
3a) To cause SRE-FILTER to "SEND" portions of files containing
server side includes (this can speed up early display on the
client end), set:
auto_header='HEAD'
delim_1.2=0
retain_bad_keyphrases='NO'
fix_expire=0
4) To add mediatypes, you will have to modify the SREF_MEDIATYPE
routine that is stored in "macrospace" (see SREFLIB1.DOC for note on this)
5) Structure of GOSERV call to this filter
Source request sel:: 151.121.65.143 80 3 151.121.65.143 1026
request GET /sampask2.htm HTTP/1.0 :
sel sampask2.htm
Example:
source : 151.121.65.143 80 4 151.121.65.143 1027
request: GET /CheckQ?searchText=very+silly+ind&andOr=and HTTP/1.0
sel: CheckQ?searchText=very+silly+indeed!&andOr=and
*/
/* ---------------- End of Description ------------------ */
/**************************************************************************************/
/*************************************************************************************/
/* ----------------- Initializations ----------------------------------*/
/* ----------------- Default initialization ---------------------*/
/* . Default values -- user can change
. (but it is recommended that INITFILT be changed instead) */
/* --------------------------------------------------------------- */
checklog='NO' /* Free entry */
inhouse_name="OUR WEB SITE" /* name we call "ourselves" */
home_name=" " /* The colloquial (not necessrily ip
name of this domain,) Note use in not_found_url */
auto_header="NO" /* no, always, head */
no_include="NO"
no_processing="NO" /* if yes, then no server side processing attempted */
delim_1.1='<!--' /* the left and right side "keyphrase" delimeters */
delim_2.1='-->' /* can be any string combo */
retain_bad_keyphrases="YES"
upload_maxsize=50 /* max size that a uploaded file can be, in k */
upload_minfree=20000 /* minimum free in K, in dowload_dir, AFTER file upload */
upload_maxsize0=upload_maxsize
upload_Minfree0=upload_minfree /*use these if error in specifying upload vars */
AUTO_NAME=0 /* YES=if request is of from /foobar/papers/
then look for /foobar/papers/papers.htm.
NO = don't check
INDEX = look for /foobar/papers/index.htm */
CHECK_alias="YES" /* check all "sels" in the alias file: YES=yes, NO=None,
HTML=.HTM (or .HTML) files only */
not_found_url='<a href="/"> Visit the '||home_name||' home page? </a> '
/* Message that is sent
along with "no such url" response*/
prefilter_result=" " /* filled in only if prefiltr called */
pre_filter="NO" /* no yes first */
post_filter="NO"
post_filter_message=" "
postfilter_name="POSTFILT" /* may be reset in initfilt */
prefilter_name="PREFILTR"
noext_type="HTM" /* NONE DIR HTM or HTML */
record_option="NO" /* YES, YES_ALL */
no_getafile_control="YES" /* who can use getafile (YES=everyone*/
allow_access="YES" /* access control on file transfers (YES=none) */
default='index.htm' /* use if default home page selected */
inhouseips.1=0 /* valid example: inhouseips.1="151.121.65" */
privset1= " "
inhouse_privs=" INHOUSE " /* additional privs for inhouseips and owners */
public_privs=" PUBLIC " /* additional privs for veryone */
max_pointdist=50 /* max distance acceptable for a "assign to point" in ncsa map */
/* Used with REPLACE:INHOUSE1, etc. */
inhouse.1=" (INHOUSE User) "
inhouse.2=' .. return to <a href="/"> home page </a>?'
superuser.1="(Super User)"
headers.1=0 /* stuff to put at beginning / end of */
footers.1=0 /* all htm documents. 0= nothing */
public_files.1=0 /* list of public_files (no logon needed to access them
actually, public_files can be an aliase
HITHERE WELCOME/INTRO.HTM
would take all requests for HITHERE and return the contents (with
ssi's) of datadir/WELCOME/INTRO.HTM
ALso, abbreviation matchine is supported
If public_files.1=0, there are none */
OPTION_hit_line=":: still access # "
/* used for error message et al */
webmaster=' (no contact available) '
/* note: you might also want to put a CONTACT line in the repstrgs_file file. */
/* Owners are automatically superusers (seperate with spaces) */
owners = 'none'
/* smtp_gateway, used by post-filter "e-mail alert" facility */
smtp_gateway=" "
ismap_url="mapimage/" /* urls that begin with this are assumed to
be responses from mappable images */
macrospace_input="YES" /* YES or NO (if NO, don't use macrospace for .IN files */
cgi_bin_dir=0 /* if 0, do not emulate cgi-bin */
messbox_dir="\GOSERV\DATA" /* SRE-FILTER data */
upload_dir=messbox_dir
/* work directory(for storage of temporary files)
should be in the data directory */
tempfile_dir="E:\gohttp\temp"
counter_file="\GOSERV\data\COUNTER.CNT"
record_all_file="\GOSERV\DATA\RECRDALL.CNT"
sendfile_file="\GOSERV\DATA\SENDFILE.CTL"
access_file="\GOSERV\DATA\ALL_FILE.CTL"
accept_range="NO"
virtual_file="\GOSERV\VIRTUAL.IN"
user_file="\GOSERV\USERS.IN"
interpret_file="\GOSERV\INTERPET.IN"
repstrgs_file="\GOSERV\REPSTRGS.IN"
alias_file="\GOSERV\ALIASES.IN"
upload_log="\GOSERV\DATA\UPLOAD.LOG"
SSI_ALLOW="YES"
SSP_ALLOW="YES"
fix_expire=0 /* set to non zero to redo response headers */
/* Change this if you install goserve in a non-standard directory */
servdir='workingdirectory'
/* servdir='drive:\working directory' */
/* This is where various "users" home directories are ( as
signified by the ~ character in a SEL). Note that the contents of
Home_dir substitue for the ~, with no syntax checking. Thus,
if Home_dir = "/USERDIR/", and the sel is /~JOES/PAGE.HTM,
the result will be //USERDIR/JOES/PAGE.HTM -- which is INCORRECT
(a correct entry would be HOME_DIR = "USER_DIR/")
Note that virtual drive replacement occurs After HOMEDIR replacement
*/
home_dir="HOMEDIR"
/* this is needed to determine default names ... */
serverport=extract(serverport)
/* the initfilt file (can be set to "" if above defaults are appropriately set) */
initfilt_file=servdir||"\INITFILT."||serverport
/* -------End of Default initialization section. -------------------- */
/*-------------- Load REXX libraries ----- */
/* Load up advanced REXX functions */
foo=rxfuncquery('sysloadfuncs')
if foo=1 then do
say 'Loading REXXUtil library '
call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
call SysLoadFuncs
end
foo=rxfuncquery('rexxlibregister')
if foo=1 then do
say ' loading REXXLIB '
call rxfuncadd 'rexxlibregister','rexxlib', 'rexxlibregister'
call rexxlibregister
end
/* load sre-filter procs into macrospace */
first_load=0 /* signal if this is first load */
foo=directory(servdir) /* reset to working directory */
/* ---------Get stuff sent from GOSERVE program -----------------*/
parse arg source, request , selx
source0=source ; request0=request ; seloriginal=selx
parse var request foo sel .
sel=strip(sel,'l','/') /* get rid of leading / */
parse var source myaddr port transaction who whoport . /* Often useful */
/* -- get macrospace libraries, or load if not there */
nosref0=1
signal on syntax name nosref
filter_name=sref_version() /* if not here, then skip to nosref */
nosref0=0
nosref: /* if skipped here, then load .rxl */
signal off syntax
if nosref0=1 then do
say " Loading SREFPRC1.RXL into macrospace "
first_load=1
tt=servdir||'\SREFPRC1.RXL'
aa=macroload(tt)
if aa=0 then do
say " ERROR: " tt " not available! " aa
audit " ERROR: " tt " not available! " aa
return "NODATA"
end
filter_name=sref_version()
say " Starting on port " serverport ": " filter_name
end
/* ------ Initialize parameters using external 'user-set' initialization file----*/
/*. Now, call the initialzation parameters external routine (INITFILT.80).
. Users are encouraged to set up initialization parameters by changing
. INITFILT, and not by changing this "main" program.
*/
if initfilt_file<>"" then
call get_initfilt 1
if default="" then do
default="INDEX.HTM" /* the traditional case */
audit " Missing DEFAULT -- check INITFILT! "
say " Missing DEFAULT -- check INITFILT! "
end
initdone: /* note: jump here if no_initfile="YES" */
/* -----------------Tidy up some initialization parameters ------------ */
current_hit.num=0 /* 0 means not counted yet */
current_hit.item=""
current_hit.mess1=""
current_hit.mess2=""
outbig=''
/* Clean up parameters that may have been set in initfilt */
inhouse=translate(inhouse_Name) /* we changed the name of INHOUSE to INHOUSE_NAME */
inhouse_name=inhouse /* to ease customization. BUt it's a pain to change in the code!*/
/* so we'll still use INHOUSE in the code */
checklog=STRIP(translate(checklog)) /* NO YES ALWAYS INHOUSE */
if auto_Name=0 then auto_name=" "
auto_name=translate(auto_name)
selautoname="" /* avoid problems with public_Files */
check_alias=STRIP(translate(check_alias)) /*NO YES HTM*/
accept_range=is_true(accept_range,'Y YES')
no_include=is_true(no_include,'Y YES')
no_processing=is_true(no_processing,'Y YES')
DEFAULT=STRIP(DEFAULT)
ismap_url=strip(translate(ismap_url)) ;
ismap_url=translate(ismap_url,'/','\')
ismap_url=strip(ismap_url,'L','/')
messbox_dir=translate(messbox_dir,'\','/')
messbox_dir=strip(messbox_dir,'t','\') /* d:\foo\bar format needed */
cgi_bin_dir=translate(cgi_bin_dir,'\','/')
cgi_bin_dir=strip(cgi_bin_dir,'t','\')
if cbi_bin_dir="" then cgi_bin_dir=0
inhouse_privs=translate(inhouse_privs)
if inhouse_privs=0 then inhouse_privs=" "
public_privs=translate(public_privs)
if public_privs=0 then public_privs=" "
replines.0=-1 /* signal that is hasn't been read in */
interp_data=0
HOME_DIR=STRIP(HOME_DIR)
virtual_file=strip(virtual_file)
virtual_dir.0=0
macrospace_input=is_true(macrospace_input,'Y YES ')
ALLOW_ACCESS=TRANSLATE(STRIP(ALLOW_ACCESS))
if abbrev(allow_access,"Y")=1 then
allow_access="YES"
ssi_allow=is_true(SSI_ALLOW,'Y YES ')
ssp_allow=is_true(ssp_allow,'Y YES ')
retain_bad_keyphrases=is_true(retain_bad_keyphrases,'YES Y ')
RECORD_OPTION=TRANSLATE(STRIP(RECORD_option))
NO_GETAFILE_CONTROL=TRANSLATE(STRIP(NO_GETAFILE_CONTROL))
DNS_CHECK=translate(dns_check)
if upload_log="" then upload_log=0
if datatype(upload_maxsize)<>'NUM' then
upload_maxsize=upload_maxsize0
if datatype(upload_minfree)<>'NUM' then
upload_minfree=upload_minfree0
pre_filter=translate(pre_filter)
post_filter=is_true(post_filter,'Y YES ')
if smtp_gateway=" " then smtp_gateway=0
cache_status="NOCACHE"
is_public=0
is_alias=0
post_message=" "
RECORD_OPTION=TRANSLATE(RECORD_OPTION)
if wordpos(record_option,'YES YES_ALL ')=0 then
record_option="NO" /* recording option on?*/
privset=public_privs /* generic privilege set */
/* check/initialize counter files, and load up input files into macrospace */
if first_load=1 then do
res1=sref_init_counter(counter_file, upload_log , record_all_file , record_option)
parse var res1 counter_file upload_log record_all_file record_option
if macrospace_input=1 then
okay=sref_load_mac(servdir,user_File,initfilt_file,repstrgs_File, ,
alias_file,interpret_file,virtual_file,access_file)
end
/* check if initfilt has been updated, if so, update macrospace */
if (first_load=0 & macrospace_input=1) | (transaction=1)then do
eek=sysfiletree(translate(initfilt_file,'\','/'),'gosh','FT')
if gosh.0>0 then do /* no file */
parse var gosh.1 atime .
filedate=sref_juldate('F',atime)
signal on error name not_load_mspace
signal on syntax name not_load_mspace
somestuff=sref_macro_initfilt()
parse var somestuff mdate .
if mdate<filedate | transaction=1 then do
say " Updating INITFILT file ... "
okay=sref_load_mac(servdir,user_File,initfilt_file,repstrgs_File, ,
alias_file,interpret_file,virtual_file,access_file)
end
end
end
not_load_mspace:
signal off syntax
signal off error
/* More global variables ............. */
tmp.0=0 /* for use by INTERPRET blocks */
crlf='0d0a'x /* might need this */
dir = datadir() /* Data directory (root of all data directories) */
/* [must include drive and end in '/'] */
wd40=translate(tempfile_dir,'/','\')
parse var wd40 (dir) data_temp_dir
data_temp_dir=translate(data_temp_dir||'/') /* don't record transfers from temp
directory of data directory */
/* other possibly useful stuff */
servername=servername()
cgi_inc_errmsg="Error in CGI Include "
cgi_inc_sizefmt="ABBREV"
cgi_inc_timefmt="ALL"
/* ---------------------- End of initialization section ------------*/
/***********************************************************************/
/***********************************************************************/
/* ---------------------- Start of active code for main filter ------*/
tempfile=dir'$'transaction'.'port /* Often used */
clientname0=who
/* Note: to view results of SAY commands, START PMPRINTF from an OS/2 window */
say 'SRE-FILTER on port ' serverport ' # ' transaction " from " who " , " whoport " : " request
/* call a pre-filter (it might EXIT back to GoServe)
The pre-filter should check for completion! */
if pre_filter="FIRST" then do
prefilter_result=do_prefiltr(' ')
parse var prefilter_result status ',' prefilter_result
a=done_it(status,"Pre-filter processed: "||prefilter_result)
if a=1 then
if post_filter=0 then
return ' '
else do
post_filter_message="Pre-filter used"
signal do_post_filter
end
end
parse var request verb uri protocol . /* split up the request line */
/* BEFORE ANYTHING ELSE, MAKE SURE THE REQUEST IS STILL ACTIVE!!
(IF CACHING OCCURED, IT MIGHT NOT BE !! */
aa=done_it(0,'From cache: '||seloriginal) /* done_it will EXIT */
if aa=1 then DO
If record_option<>"NO" then do
if record_option="YES" then
parse var seloriginal doit '?' .
else
doit=seloriginal
foo=sref_lookup_count(record_all_file,doit,'ADD','OK',2)
end
if post_filter=0 then
return ' '
else do
post_filter_message="Cached file sent:"||seloriginal
signal do_post_filter
end
END
/* Check request for basic grooviness*/
if left(protocol,4)\='HTTP' & protocol\='' then do
if post_filter=0 then
return response('badreq', 'specified a protocol that was not HTTP')
else do
dog=response('badreq', 'specified a protocol that was not HTTP')
dog
post_filter_message='Bad HTTP Protocol '
signal do_post_filter
end
end
/* Is it one of the PUBLIC_FILES ??. IF so, skip logons, requests
string modifications, and send it (but DO ssi's) */
tsel=translate(sel)
gotit=0 ; doexact=0
do m=1 to 100000 /* look in public_files list */
if symbol('public_files.m')<>"VAR" then leave
ares=sref_wildcard(tsel,public_files.m,doexact)
parse var ares astat "," aurl ; astat=strip(astat)
if astat=0 then iterate /* no match */
usesel=sel
gotit=m
usesel=sel
if ares=1 then
leave /*first exact match rules */
else
doexact=1
end
if gotit>0 then do /* if gotit, then reset the sel */
sel=usesel
say " PUBLIC_FILE. " gotit " being used: " sel
is_public=1 ;
home_dir="" ; auto_name=" " /* just to be safe */
privset=" PUBLICFILE PUBLIC " /*getafile uses this to suspend no_getafile_control*/
cache_status=' ' /* it's public, so you might as well allow caching */
/* Do we record all "not unallowed" requests */
if record_option<>"NO" then do
if record_option="YES" then
parse var seloriginal doit '?' .
else
doit=seloriginal
foo=sref_lookup_count(record_all_file,doit,'ADD','OK',2)
end
signal do_alias /* do the verb (skip logons, request mods, etc */
end
/* if here, not a public_file ... */
/* ---------------- Check for logon rights and privileges --------- */
afoo:
/* check to see if requester is an unallowed ips */
call badips(who)
if result=1 then do /* he is a bad ips, but we let owner and inhouse override it */
if wordpos(who,owners)=0 then do /* no an owner */
call goodips(who)
if result=0 then do
if post_filter=0 then
return response('unauth', 'You are not permitted access to this server ')
else do
dog=response('unauth', 'You are not permitted access to this server ')
dog
post_filter_message="Unauthorized access "
signal do_post_filter
end
end
end
end
/* Do an DNS check? */
if dns_check="YES" then do
clientname0=clientname()
if clientname0=who then do
audit ' Denied access to ' who
if post_filter=0 then
return response('unauth', 'No client name found, access to this server is denied. ')
else do
dog=response('unauth', 'No client name found, access to this server is denied. ')
dog
post_filter_message="No client name found, access denied "
signal do_post_filter
end
end
say " DNS check ok: " clientname0
end
/* should we check for logon rights */
select
when checklog="YES" & (sel=" " | left(strip(sel),1)="/") then do
dologon=1
cache_status=' NOCACHE '
end
when checklog="INHOUSE" then do
dologon=1
cache_status=' NOCACHE ' /* do NOT let goserve cache */
end
when checklog="ALWAYS" then do
dologon=1
cache_status=' NOCACHE '
end
otherwise do
dologon=0
if allow_access="YES" then cache_status=' '
end
end
/* do logons, etc ... */
if wordpos(who,owners)>0 then do /* owners are treated with kid gloves */
privset="SUPERUSER INHOUSE "||inhouse_privs
username="OWNER" ;
end
else do /* non owners get the third degree? */
if dologon=1 then do /* yup, book 'em */
userinfo=do_logon(who,checklog) /* if no match, exits (for new pwd or say sorry) */
parse var Userinfo username privset
/*very tough--only inhouse allowed (explicit or as a privilege)?*/
if checklog="INHOUSE" & wordpos('INHOUSE',privset)=0 then do
call lineout tempfile, '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
call lineout tempfile, "<html><head><title>Site Restricted to " inhouse " Staff</title>"
call lineout tempfile, "</head>"
call lineout tempfile, "<body><h2>Currently Restricted to " inhouse " Staff!</h2>"
call lineout tempfile, " Sorry, but until clearance details are worked out, this site "
call lineout tempfile, " is restricted to ' inhouse ' staff only. <b> "
call lineout tempfile, " If you have questions, please contact: "
call lineout tempfile, ' <address> ' webmaster ' </address>'
call lineout tempfile, " </body> </html> "
call lineout tempfile
if post_filter=0 then
return 'FILE ERASE TYPE text/html NAME ' tempfile
else do
dog='FILE ERASE TYPE text/html NAME ' ||tempfile
dog
post_filter_message="Logon denied to non-inhouse user "
signal do_post_filter
end
end
/* If here, welcome ! */
say ' Access by : ' username '(privs: ' privset
end /* dologon =1 */
/* If here, then no logons required, */
else do
call goodips(who) /* is this an inhouse connect - get some privs! */
isinhouse=result
if isinhouse=0 then
privset=" "
else
privset="INHOUSE "||inhouse_privs||privset1
end /* check if logon rwquired */
end /* not superuser */
/* add public_privs to everyone */
privset=privset||' '||public_privs
/* now check on allow_access conditions, for this request string.
Note this will also check stuff after the ?
(use of * can circumvent this) */
okay1=sref_allow_access(seloriginal,allow_access,access_file,privset)
/* if not permiited, return appropriate message */
if okay1=0 then do
got1=not_allowed_message(clientname0,1)
got1='FILE ERASE TYPE text/html NAME '||tempfile
if post_filter=0 then
return got1
else do
got1
post_filter_message="No privileges for this request: "||sel
signal do_post_filter
end
end
/* Now check for ssi and ssp privs */
if ssi_allow<>1 then do
aok=wordpos('NO_SSI',translate(okay1))
if aok<>0 then
no_include=1
end
if ssp_allow<>1 then do
aok=wordpos('NO_SSP',translate(okay1))
if aok<>0 then
no_processing=1
end
/* otherwise, proceed as normal */
/* Do we record all "not unallowed" requests (portion ? */
if record_option<>"NO" then do
if record_option="YES" then
parse var seloriginal doit '?' .
else
doit=seloriginal
foo=sref_lookup_count(record_all_file,doit,'ADD','OK',2)
end
/* ----------------------------------------------------------------*/
/* ----------- If here, logon okay and we have privileges ---------------- */
/* Steps:
0) Call prefilter ?
1) See if empty request. If so, SEL=Default
2) If check_alias is on, then see if this sel is an alias for some other sel.
If so, use this other sel
If VERB <> GET, jump to step 5
3) Syntax check: is this a "directory request?" (no ?, and no . in the
final portion of the request string)
If so, append a / to the end
4) See if this SEL is an "extension free" name
(but not the root directory though).,
If a at end (i.e. xxx/yyy/.
check autoname for candidate "directory's default document" names
If not, check NOEXT_TYPE; if DIR do above, otherwise, add appropriate
extension.
5) Process the verb, using the SEL from steps 2-5.
5a) If the SEL is for a document which does not exist (and VERB=GET)
go back to autotname and try again
*/
/* call a pre-filter (it might EXIT back to GoServe) */
if pre_filter="YES" then do
prefilter_result=do_prefiltr(privset)
parse var prefilter_result status "," prefilter_result
a=done_it(status,"Pre-filter processed:"||prefilter_result) /* has the request been processed*/
if a=1 then
if post_filter=0 then
return ' '
else do
post_filter_message="Pre-filter used"
signal do_post_filter
end
end
/* Check 'sel', see if it means "home page time" (root directory's default document). */
if sel=' ' then
sel=default
else
if substr(sel,1,1)='?' then /* simple, or with options */
sel=default||sel /* default MUST be an HTML document */
/* Now see if the ACTION is really an alias for some other
action. This is especially useful if an <ISINDEX> type of
searchable index has been responded to, which will have SEL
that typically looks like "COOLJUNK.HTM?A+B+C --
.. by default (with no alias checking),
this filter will assume that this is a request for the
HTML document COOLJUNK.HTM, with "options" A B C.
(in a sense, the blank and / request strings are aliases for DEFAULT).
*/
do_alias: nop /* jump here if is_public */
if check_alias<>"NO" then do /*see if 'action' is an alias for something else*/
if check_alias="YES" then
doit=1
else do
amtype=sref_mediatype(action)
if left(check_alias,3)="HTM" & amtype="text/html" then
doit=1
end
if doit=1 then do /* yes, check for an alias */
what1=alias_action(sel)
parse var what1 gotit sel
sel=strip(sel)
if gotit > 0 then do /* got a match */
say " Alias # " gotit " resolves to: " sel
is_alias=1
/* is this a "redirect" action? */
if abbrev(translate(sel),'HTTP://')=1 then do
if post_Filter=0 then
return moved(sel,302)
else do
dog=moved(sel,302)
dog
post_filter_message="Alias invoked temporary redirect "
signal do_post_filter
end
end
/* permanent move */
if abbrev(translate(sel),"!MOVED")=1 then do
taction=translate(sel)
if abbrev(taction,'!MOVED=')=1 then
action=delstr(sel,1,7)
else
if abbrev(taction,'!MOVED')=1 then
action=delstr(sel,1,6)
sel=sref_fix_url(action,servername,serverport)
if post_filter=0 then
return moved(sel,301)
else do
dog=moved(sel,301)
dog
post_filter_message="Alias invoked permanent redirect "
signal do_post_filter
end
end
/* temporary move */
if abbrev(translate(sel),"!TEMP")=1 then do
taction=translate(sel)
if abbrev(taction,'!TEMP=')=1 then
action=delstr(sel,1,6)
else
if abbrev(taction,'!TEMP')=1 then
action=delstr(sel,1,5)
sel=sref_fix_url(action,servername,serverport)
if then
return moved(sel,302)
else do
dog=moved(sel,302)
dog
post_filter_message="Alias invoked temporary redirect "
signal do_post_filter
end
end
/* is this a "send non-data directory file" action (OBSOLETE, but ...)*/
if abbrev(translate(sel),"!TRANSFER")=1 then do
parse var sel action '?' awords
if post_filter=0 then
return send_non_datadir(action,awords)
else do
dog=send_non_datadir(action,awords)
dog
post_filter_message="Transfered non-data directory file"
signal do_post_filter
end
end
/* Note: these obsolete functions are now supported, by directory, by
sref_dovirtual. However, for single urls, it can be useful to use them
(rather then specifying a "directory" in the virtual_file */
/* else, just fix up \'s */
parse var sel action '?' awords
action=strip(translate(action,'/','\'))
if pos('?',sel)<>0 then
sel=action||'?'||awords
else
sel=action
end /* gotit > 0 */
end /* doit=1 */
end /* alias checking */
if is_public=1 then signal do_verb /* public_files are not autonamed */
/* The next stuff is for GET requests only, so skip otherwise */
if verb<>"GET" | left(sel,1)="!" then signal do_verb
/* Check sel to see if it's a "no extension" variant. If so, NOEXT_TYPE dictates
what to do.
Note that if ?xx appears after a extension-free action (say,
yyy/xxx?abc), then we assume that this is an action request.
Otherwise...
DIR: treat it as a "non-root directory's default document, but they forgot
the final /".
HTM or HTML : Add .HTM or .HTML respectively
NONE : leave it be
*/
/* if ? appears, leave it be:
ether its an action name,
or it's a xx/?yyy (which is seen as xx/)
*/
if pos('?',sel)=0 then do
if right(sel,1)<>'/' then do /* it doesn't end with a */
foo2=translate(sel,' ','/')
lastword=word(foo2,words(foo2)) /* extract the last piece */
foo2=pos('.',lastword)
noexttype=translate(noext_type)
if foo2=0 then /* is no period in the last piece */
select
when noexttype="DIR" then do
sel=sel||'/' /* tIt's a directory*/
say " Using re-named Sel: " sel
end
when noexttype="HTM" then do
sel=sel||".HTM" /* fat style html file */
say " Using re-named Sel: " sel
end
when noexttype="HTML" then do
sel=sel||".HTML" /* html file */
say " Using re-named Sel: " sel
end
when noexttype="NONE" then do
nop /* leave it be */
end
otherwise do
sel=sel||noext_type /* user specified (experimental!) */
say " Using re-named Sel: " sel
end
end /* if select (sel does not end with name.ext) */
end /* does not end in / */
end /* no ? in sel */
/* See if we should look for sels of the type /xxx/yyy/, (no html file,
just a directory, and what file to use if we find such a beast */
selautoname=sel /* may need to reset sel (see GET verb processing below) */
if abbrev(translate(sel),"CGI-BIN")=1 | pos(translate(ismap_url),translate(sel))>0 then
auto_name="" /* never check if a mapimage or cgi-bin call */
goautoname: /* hop here if no match, auto_name <> "" */
if auto_name<>" " then do /* some candidate defaults? */
parse var sel sel1 '?' stuff /* get out name portion */
sel1=strip(sel1) /*be neurotic, strip out spaces */
lensel1=length(sel1)
foo1=substr(sel1,lensel1) /*is last character a /, then maybe do autoname */
if foo1='/' then do /* a request for default document */
tryme=strip(word(auto_name,1))
auto_name=delword(auto_name,1,1) /* remove it from list */
select
when translate(tryme)="!CREATE" then do /* create a list of links to files*/
no_getafile_control="YES"
sel1='getafile?dir=/'||sel1||'&gifs=YES&showdate=YES&showsize=YES&rootdir=!&iscreate='||sel
end
when translate(tryme)="!CREATE*" then do
no_getafile_control="YES"
sel1='getafile?dir=/'||sel1||'&gifs=YES&showdate=YES&showdir=YES&rootdir=!&showsize=YES&iscreate='||sel
end
when abbrev(tryme,'*')=0 then do
if pos('/',tryme)=1 | pos(':',tryme)>0 then
sel1=tryme
else
sel1=sel1||tryme
end
otherwise do /* *, *.htm, *.html, etc. */
foo2=lastpos('/',sel1,lensel1-1)
parse var tryme ast "." ext
if ext="" then ext=".htm"
sel1=sel1||substr(sel1,foo2+1,lensel1-(1+foo2))||'.'||ext
end
end /* select */
say "Trying auto-named sel = " sel1
sel=sel1
end
else /* not a "request for directory */
auto_name=' ' /* so no need to check later */
end /*we may loop here several times, but eventually
the auto_names list will be exhausted */
do_verb: nop /* jump here avoids AUTONAME for POST and HEAD */
/* ---------- Process a standard (GET,POST, or HEAD) request --------------- */
/* See if a "home_dir" flag (a ~) is present. If so, replace
it with the "home_dir". Note that home_dir is set above, and the replacementm
is literal -- there is NO checking of proper placement of /'s. So,
be careful that your use of the ~ in request strings is consistent with
the value you give to home_dir */
parse var sel action '?' awords
foo=action
action=sref_replacestrg(action,'~',home_dir) ;
if foo<>action then say " Home_dir set in sel: " sel
if pos('?',sel)>0 then
sel=action||'?'||awords
else
sel=action
/* used if this is a file */
parse var sel action '?' awords
/* .... Now carry out supported verbs (GET POST HEAD), or SPECIAL control */
/* -- and check for cgi_bin calls */
/* first, see if cgi-bin call (and cgi-bin emulation is on */
if (abbrev(translate(sel),'CGI-BIN')=1) & (cgi_bin_dir<>0) & (verb="GET" | verb="POST") then do
/* note: we convert cgi-bin/mapimage calls to /mapimage -- this assumes
that the .map file will be in xxx/yyy.zzz (when sel is cgi-bin/mapimage/xxx/yyy.zzz)
We may change this later */
if abbrev(translate(sel),'CGI-BIN/MAPIMAGE') then
sel=delstr(sel,1,8) /* drop the cgi-bin/ */
else do
selt=translate(sel,' ','\/')
what1=alias_action(word(selt,2))
parse var what1 gotit new_dir
if gotit<>0 & new_dir<> "" then do
cgi_bin_dir=strip(translate(new_dir,'\','/'),'t','\')
end
cmdfile=dostempname(tempfile_dir||'\f????.cmd')
gotit=sref_docgi(cgi_bin_dir, sel, verb, clientname0, filter_name, serverport , ,
servername, protocol, dir, who,tempfile,cmdfile)
if post_filter=0 then
return gotit
else do
gotit
post_filter_message="CGI-BIN call: "||sel
signal do_post_filter
end
end /* not a cgi-bin/mapimage call */
end /* is a cgi-bin call */
/* Second, Check if it is a special 'control' requests.
For these, the verb is ignored (though would usually be GET).
Note that this must be in the main, since it returns to the
goserve server directly. Also, we may need to check on privileges
*/
if left(sel,1)='!' then do
parse var sel sel '?' args /* there may be some arguments */
sel=translate(sel)
select
when sel='!PING' then do
post_filter_message="Ping request"
'STRING Ping!'
end
when sel="!MACRO" then do
if ispriv("CONTROL")=1 then do
call lineout tempfile, '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
call lineout tempfile, "<html><head><title>SRE-Filter Macrospace Update </title>"
call lineout tempfile, "</head>"
call lineout tempfile, "<body><h1> Updating SRE-Filter macrospace...</h1>"
if macrospace_input=1 then do
okay=sref_load_mac(servdir,user_File,initfilt_file,repstrgs_File, ,
alias_file,interpret_file,virtual_file,access_file)
post_filter_message='Macrospace updated '
end
else do
post_filter_message='Macrospace update not permitted '
end
call lineout tempfile, post_filter_message
call lineout tempfile, " </body> </html> "
call lineout tempfile
'FILE ERASE TYPE text/html NAME ' tempfile
end
else do
post_filter_message="!Special request denied: "||sel
'FILE ERASE TYPE text/html NAME ' tempfile
end
end
when sel='!STATISTICS' then do
post_filter_message="CONTROL Statistics request"
'CONTROL STATISTICS'
end
when left(sel,5)='!HOST' then do
if args<> " " then sel="!HOST?"||args
sel=translate(sel,' ','?+&')
sel=word(sel,2)
host(sel) /* a ping */
post_filter_message="Host request"
end
when sel='!SAVE' then
if ispriv("CONTROL")=1 then do
'CONTROL MOVEAUDIT'
post_filter_message="Control Moveaudit"
end
else do
post_filter_message="!Special request denied: "||sel
'FILE ERASE TYPE text/html NAME ' tempfile
end
when sel='!RESET' then
if ispriv("CONTROL")=1 then do
'CONTROL RESET ALL'
post_filter_message="Control Reset all"
end
else do
post_filter_message="!Special request denied: "||sel
'FILE ERASE TYPE text/html NAME ' tempfile
end
when sel='!VARIABLE' then
if ispriv("SUPERUSER")=1 then do
show_vars(args) /* call as routine-- needs globals */
post_filter_message="Variable display"
end
else do
'FILE ERASE TYPE text/html NAME ' tempfile
post_filter_message="!Special request denied:"||sel
end
when sel="!ASKMESSBOX" then do
if ispriv("VIEWMESS") then do
post_filter_message='Message box access'
boy=what_messbox(args)
end
else
post_filter_message='Message box access denied'
/* tempfile is an error message (if not ispriv), or a list of known mailboxes */
'FILE ERASE TYPE text/html NAME ' tempfile
end
when sel="!VIEWMESS" then do
post_filter_message='Message box viewing'
doit=viewmessage(args) /* do it is the goserv command */
doit
end
otherwise do
post_filter_message='Special request processing unknown:'||sel
dog= response('badreq', 'asked for unknown Special control "'sel'"')
dog
end
end /*select */
if post_filter=0 then
return ' '
else
signal do_post_filter
end /* Special ! requests */
/* end of ! special requests **********/
/* Now, do the verbs ! */
select
/* ------ HEAD REQUESTS ------- */
when verb='HEAD' then do
parse var sel action '?' awords
file=do_virtual_file(dir,action) /* virtual_dir. is exposed */
if file="!MOVED" then do
post_filter_message="HEAD request: "||seloriginal
signal do_post_filter
end
if file=0 then do
if post_filter=0 then
return not_found_response(seloriginal,'Document not found '," ")
else do
dog=not_found_response(seloriginal,'Document not found '," ")
dog
post_filter_message="HEAD request: Document not found: "||seloriginal
signal do_post_filter
end
end
else do
if auto_header<>"NO" then do
mtype=sref_mediatype(file)
if mtype="text/html" then do
foo= do_auto_header(file,0) /* do_auto_header will generate the responses */
/******************
Perhaps readd this later?
'HEADER DROP Content-Length:'
asimov=chars(file)-1
'HEADER ADD Content-Length:'asimov
***************************************************/
end
end
if post_filter=0 then
return 'NODATA'
else do
'NODATA'
post_filter_message="Head request "
signal do_post_filter
end
end
end
/* -------- GET REQUESTS ---------*/
/* Simple transfer of files (no forms, etc. involved) must use GET verb */
/* Suggestion: to be safe, files subject to server side includes (ssi) should be syntatically
correct HTML documents, even if no ssi's occur (thus protecting against
errors, however unlikely, in the ssi routine). The best way
to achieve this is to set your "delimiters" to be <!-- and -->, and
to NOT have spaces in your keyphrase:
Example: <!-- REPLACE:DATE -->
(in processing ssi's, this filter treats : = and spaces equivalently)
*/
when verb='GET' then do /* GET is used for file transfers */
parse var sel action '?' awords
action=TRANSLATE(strip(action)) /* CAP AND Rid spaces */
/* There are 4 classes of GET requests supported:
1) Simple file name eg; /ZIPFIP/ZIPFIP.HTM
File is retrived, with possible ssi's
2) File name with OPTIONS, eg; /OVERVIEW.HTM?From+ZIPFIP+main+page
File is retrieved, with possible ssi's (some of which may use these OPTIONS
3) Form name with parameters, eg; /CALC/DOCALC?12-34%2B51
sref_getpost is called with appropriate arguments
4) Mappable image (ISMAP) response. URL must include the
ismap_url string (say, MAPIMAGE/) and end with a ?xx,yy.
Eg ISMAPDIR/US/USMAP?41+239
Note: ISINDEX type searchable-index requests are identical in
appearance to case 2. To avoid this problem, use the alias_file
mechanism (described above) to convert to a class 3
( "form name" SEL) that requests DOSEARCH
*/
if pos('?',sel)=0 then /* no argument list, must be case 1 */
jcase=1
else do /*arguments, class 2,3, or 4 */
attype=sref_mediatype(action)
if attype='text/html' then do /* CLASS 2 ? */
jcase=2 /* class 2: html with "OPTIONS" */
optlist.0= make_optlist(awords) /* optlist. is exposed */
end
else do /* CLASS 3 or 4 */
jcase=3 /* case 3: form */
if pos(translate(ismap_url),action) > 0 then
jcase=4 /* class 4: ismap */
end
end
/* A file (perhaps with options) transfer*/
if jcase < 3 then do
file=do_virtual_file(dir,action) /* virtual_dir. is exposed */
if file="!MOVED" then do
post_filter_message="GET request: "||seloriginal
signal do_post_filter
end
if file=0 then do
if auto_name=" " | is_public=1 then do /* no directory specific defaults */
if post_Filter=0 then do
return not_found_response(seloriginal,' Document not found ',not_found_url)
end
else do
dog= not_found_response(seloriginal,' Document not found ',not_found_url)
dog
post_filter_message="Document not found "
signal do_post_filter
end
END
else do /* more directory specific defaults to test? */
sel=selautoname
signal goautoname
end
end /* file = 0 */
/* File exists! */
/* Do server side includes ? */
/*accept_range=0*/
atype=sref_mediatype(file)
if atype<>'text/html' then do /* NOTE: Includes ONLY for html type files */
dog=1
if accept_range=1 then do
dog=process_range(file,atype)
end
if accept_range<=0 | dog=0 then do
dog='FILE TYPE '||sref_mediatype(file)||" "|| cache_status ||' NAME '|| file
end
fooo=stream(file,'c','close')
if post_filter=0 then do
dog
return ' '
end
else do
dog
post_filter_message='Non-HTML File sent:'||file
signal do_post_filter
end
end
/* else, make temp file with possible includes, or maybe not */
docname=action /* (might be used in do_includes */
call do_includes file /* many globals used, so call as a routine */
dofile1=result
if dofile1=0 then do /* no changes needed (or none attempted), send back requested file */
dog=1
if accept_range=1 then
dog=process_range(file,atype)
if accept_range=0 | dog=0 then
dog='FILE TYPE ' ||sref_mediatype(file)||' '|| cache_status ||' NAME '|| file
if auto_header="ALWAYS" then
foo= do_auto_header(file,0)
if post_filter=0 then do
/* foo=send_bits(file) */
dog
/*say " returning "*/
return ' '
end
else do
dog
post_filter_message='HTML File sent:'||file
signal do_post_filter
end
end
else do /* changes were made/results returned, using VAR ,in do_includes */
if post_filter=0 then do
return ' '
end
else do
post_filter_message='HTML File sent (with ssi):'||file
signal do_post_filter
end
end
end /* jcase < 3, file transfer (with includes? */
/* Note on cache_status. If logon required for everyone/always, or
if inhouse, then do NOT cache this file (if cached, a lucky requester
could get it even if not allowed. Note that stuff with server side
includes is NEVER cached (it's returned as a VAR, which GOSERVE does
not cache).
Also, if byte range sent, no caching.
*/
/* -------------------------------------------------
Else, it's the result from Image or Form (3 or 4)
... so call an external procedure to do it
*/
/* is this a mappable image request */
if jcase=4 then do
mapfile0=sref_replacestrg(action,ISMAP_url,"")
mapfile=do_virtual_file(dir,MAPFILE0,1)
/* note that this construction handles href="mapimage/bob.map" in
file with url /work/hi.htm (which would generate call to url of
/work/mapimage/bob.map )
*/
if file<>"!MOVED" then
foo=sref_mapimage(mapfile,awords, servername, serverport, tempfile, dir, max_pointdist)
if post_filter=0 then
return ' ' /* completion code invoked in mapimage */
else do
post_filter_message='Mappable image request:'||foo
signal do_post_filter
end
end
/* else, jcase=3, ... it's an action request (i.e.; a FORM response) ... */
/* first, determine if any extra parameters are need, based on ACTION */
infiles=access_file||','||user_file||','||virtual_file||','||alias_file||','||sendfile_file
select
when action="SENDFILE" then
params=allow_access||' '||macrospace_input
when action="GETAFILE" then
params=allow_access||' '||no_getafile_control|| ' '||macrospace_input
when action="GET_URL" | action="PUT_FILE" then
params=upload_dir||' '||upload_maxsize||' '||upload_Minfree||' '||upload_log
when action="MESSAGE" then
params=0
when action="DOSEARCH" then
params=macrospace_input
otherwise do /* add directory, info */
ACTION=do_virtual_file(SERVdir,action,1)
if action="!MOVED" Then do
post_filter_message='GET request:'||action
signal do_post_filter
end
params= macrospace_input
END
end
/* should we record ALL GET / POST requests ? */
if no_processing<>1 then DO
got1=sref_getpost(tempfile,ACTION,awords,verb,uri,who, ,
servdir,messbox_dir,dir,tempfile_dir, ,
webmaster,privset,params,infiles)
END
else
got1=response(401,' Server side processing privileges not granted for: '||action)
fpp=stream(tempfile,'c','close')
if post_filter=0 then do
return got1 /* got1 is the file erase string */
end
else do
got1
post_filter_message='GET request:'||action
signal do_post_filter
end
end /* get */
/* -------- POST REQUESTS ---------*/
when verb='POST' then do /** POST is used for forms */
/* Check for arguments. Note that if no arguments, error. */
drop awords
'read body var awords' /* get the incoming data */
if rc=-4 then /* body too large */
if post_filter=0 then
return response('badreq', 'sent too much data')
else do
dog= response('badreq', 'sent too much data')
dog
post_filter_message="POST error: too much data"
signal do_post_filter
end
if rc<>0 then /* e.g., invalid HTTP header */
if post_filter=0 then
return response('badreq', 'sent data that could not be read')
else do
dog=response('badreq', 'sent data that could not be read')
dog
post_filter_message="POST error: could not read data "
signal do_post_filter
end
ACTION=translate(sel)
infiles=access_file||','||user_file||','||virtual_file||','||alias_file||','||sendfile_file
select
when action="SENDFILE" then
params=allow_access||' '||macrospace_input
when action="GETAFILE" then
params=allow_access||' '||no_getafile_control|| ' '||macrospace_input
when action="GET_URL" | action="PUT_FILE" then
params=upload_dir||' '||upload_maxsize||' '||upload_Minfree||' '||upload_log
when action="MESSAGE" then
params=0
when action="DOSEARCH" then
params=macrospace_input
otherwise do /* add directory, info */
ACTION=do_virtual_file(SERVdir,action,1)
if action="!MOVED" Then do
post_filter_message='POST request:'||action
signal do_post_filter
end
params= macrospace_input
end
end
/* should we record ALL GET / POST requests ? */
if no_processing<>1 then DO
got1=sref_getpost(tempfile,ACTION,awords,verb,uri,who, ,
servdir,messbox_dir,dir,tempfile_dir, ,
webmaster,privset,params,infiles)
END
else
got1=response(401,' Server side processing privileges not granted for: '||action)
fpp=stream(tempfile,'c','close')
if post_filter=0 then
return got1
else do
got1
post_filter_message='POST request:'||sel
signal do_post_filter
end
end /* post */
otherwise do
if post_filter=0 then
return response('badreq', 'sent an unknown verb "'verb'"')
else do
dog=response('badreq', 'sent an unknown verb "'verb'"')
dog
post_filter_message="Sent an unknown verb "
signal do_post_filter
end
end /* select verb */
do_post_filter:
IF POST_FILTER=0 THEN RETURN ' '
signal on syntax name bad2
signal on error name bad2
yow='foo3='||postfilter_name||'(post_filter_message,source0,request0,seloriginal,tempfile,smtp_gateway)'
interpret yow
signal off syntax; signal off error
return ' '
bad2:
signal off syntax; signal off error
say " Error in post_filter routine "
return ' '
/* ---------------- END of MAIN routine [cannot reach here] -------------- */
/*************************************************************************/
/* ============================================================= */
/* routines to read in (from file or macrospace) initialization files */
/* ============================================================= */
/* ---------------------------------------------- */
/* read an .in file, or it's macrospace version, into filelines. stem
variable
atype:
INITFILT, REPSTRGS, INTERPRET, ALIASES, USER
*/
/* ---------------------------------------------- */
file_or_macro:
parse upper arg atype
somestuff=0
crlf='0d0a'x
select /* which .in file */
when atype="INITFILT" then get_file=initfilt_file
when atype="REPSTRGS" then get_file=repstrgs_file
when atype="INTERPRET" then get_file=interpret_file
when atype="ALIASES" then get_file=alias_file
when atype="VIRTUAL" then get_file=virtual_file
when atype="ACCESS" then get_file=access_file
when atype="USER" then get_file=user_file
otherwise do
filelines.0=0
return 0
end
end
/* skip if suppresion of mspace for input */
if macrospace_input=0 then signal no_macro2
signal on error name no_macro /* which macro */
signal on syntax name no_macro
gotit=0
select
when atype="INITFILT" then somestuff=sref_macro_initfilt()
when atype="REPSTRGS" then somestuff=sref_macro_repstrgs()
when atype="INTERPRET" then do
somestuff=sref_macro_interpret()
end
when atype="ALIASES" then somestuff=sref_macro_aliases()
when atype="VIRTUAL" then somestuff=sref_macro_virtual()
when atype="ACCESS" then somestuff=sref_macro_access()
when atype="USER" then somestuff=sref_macro_user()
otherwise somestuff=0
end
gotit=1
no_macro:
if gotit=0 then
say "Could not load from macrospace:"atype /* else, signal error */
no_macro2: /* skip here if macrospace_input=no */
signal off error ; signal off syntax
/* get date of file */
eek=sysfiletree(translate(get_file,'\','/'),'gosh','FT')
if gosh.0=0 then do /* no file */
say " Missing initialization file = " get_file atype
filelines.0=0
return 0
end
parse var gosh.1 atime .
filedate=sref_juldate('F',atime)
/* get date of macro_space version */
if somestuff<>0 then
parse var somestuff macrodate "," somestuff
else
macrodate=0
if transaction=1 then macrodate=0 /* a hack-- on first tranaction re-read*/
/* use newer one */
if filedate>=macrodate then do /* if file newer then macrospace, use file */
if macrospace_input=1 then
say " WARNING: Need to update macro space:" atype ", file macro " filedate macrodate
ause=fileread(get_file,'templines',,'E')
if (ause=0) then do /*no such file,*/
say " Unavailable file: " get_file atype
filelines.0=0
return 0
end /* so no user defined replacement strings*/
isfrom=" file "
end
else do /* use stuff stored in macro space */
i1=0
do until somestuff=""
i1=i1+1
parse var somestuff templines.i1 (crlf) somestuff
end
templines.0=i1
isfrom=" macro "
end
iff=0
do mm=1 to templines.0
aline=strip(templines.mm)
if left(aline,1)=';' | aline="" then iterate
iff=iff+1
filelines.iff=sref_replacestrg(aline,"`~","'",'ALL')
end
filelines.0=iff
return iff
* --------------------------------------------------- */
/* Read / interpret initfilt (from file or macrospac */
/* --------------------------------------------------- */
get_initfilt:
call file_or_macro 'INITFILT'
/*Allow for gratuitous coding calamity by clumsy users */
mm=0 ; nerrs=0
iat1: nop
if mm>=filelines.0 then do
signal off syntax
signal off error
signal off failure
return nerrs
end
mm=mm+1
signal on syntax name foobar1
signal on error name foobar1
signal on failure name foobar1
goo=filelines.mm
ok=0
interpret goo
ok=1
foobar1:
if ok=0 then do
nerrs=nerrs+1
say " Error in initfilt: "goo
audit" Error in initfilt: "goo
end
signal iat1
return 0
* --------------------------------------------------- */
/* Read interpret mini-code-blocks (from file or macrospac */
/* --------------------------------------------------- */
get_interpret:
call file_or_macro 'INTERPRET'
if filelines.0 =0 then
return
else
interp_data=filelines.1
do mm=1 to filelines.0
interp_data=interp_data||crlf||filelines.mm
end
return filelines.0
/* -------------------------------------------- */
/* Setup replacestrg lines */
setup_replines: /* routine to setup replines. stem variable */
call file_or_macro 'REPSTRGS'
/* Got info, so create the replines. stem variable */
iat=0
do ii =1 to filelines.0
aline=strip(filelines.ii)
parse var aline alabel avalue
kgot=0
do jj=1 to iat
if alabel=replines.jj.label then do /* multi lines replacements? */
replines.jj.value=replines.jj.value||crlf||avalue
kgot=1
leave
end
end
if kgot=0 then do /* new entry */
iat=iat+1
replines.iat.label=alabel
replines.iat.value=avalue
end
end
replines.0=iat
return iat
/* ---------------------- Endo of access control routines -------------------- */
/*****************************************************************************/
/**************************************************************************/
/* -------------------------Generic response stuff, from GOFILTER.80- */
/* ----------------------------------------------------------------------- */
/* RESPONSE: Standard [mostly error] responses. */
/* ----------------------------------------------------------------------- */
/* This routine should stay in the main filter program. */
/* Arguments are: response type and extended message information. */
/* It returns the GoServe command to handle the result file. */
response: procedure expose tempfile seloriginal request0 source0
parse arg request, message
select
when request='badreq' then use='400 Bad request syntax'
when request='notfound' then use='404 Not found'
when request='forbid' then use='403 Forbidden'
when request='unauth' then use='401 Unauthorized'
otherwise do
use='404 Not found'
say 'weird response ' request message
end
end /* Add others to this list as needed */
/* Now set the response and build the response file */
'RESPONSE HTTP/1.0' use /* Set HTTP response line */
parse var use code text
call lineout tempfile, '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
call lineout tempfile, "<html><head><title>"text"</title></head>"
call lineout tempfile, "<body><h2>Sorry...</h2>"
call lineout tempfile, "<p>The request from your Web client" message"."
call lineout tempfile, "<hr><em>HTTP response code:</em>" code '['text']'
call lineout tempfile, "<br><em>From server at:</em>" servername()
call lineout tempfile, "<br><em>Running:</em>" server()
call lineout tempfile, "</body></html>"
call lineout tempfile /* close */
return 'FILE ERASE TYPE text/html NAME' tempfile
/* ----------------------------------------------------------------------- */
/* NOT_FOUND_RESPONSE: Return a "not found" response,
. with optional message */
/* ----------------------------------------------------------------------- */
not_found_response: procedure expose tempfile source0 seloriginal request0
parse arg request, message , message2
'RESPONSE HTTP/1.0 404 Not found' /* Set HTTP response line */
call lineout tempfile, '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
call lineout tempfile, "<html><head><title>"message"</title></head>"
call lineout tempfile, "<body><h2>Sorry...</h2>"
call lineout tempfile, "<p>Unable to complete the request from your Web client:" request"."
call lineout tempfile, "<BR> Problem: " message
call lineout tempfile, "<p> " message2
call lineout tempfile, "<p><em>From server at:</em>" servername()
call lineout tempfile, "<br><em>Running:</em>" server()
call lineout tempfile, "</body></html>"
call lineout tempfile /* close */
return 'FILE ERASE TYPE text/html NAME' tempfile
/* ----------------------------------------------------------------------- */
/* MOVED: Return a 'moved' response */
/* ----------------------------------------------------------------------- */
/* This must be in the main filter program (uses PORT and TEMPFILE). */
/* Argument is new URL or partial URI */
/* It returns the GoServe command to handle the result file. */
moved: procedure expose serverport tempfile source0 seloriginal request0
parse arg uri , mtype
port=serverport
if left(translate(uri),5)=='HTTP:' then /* got full URI */ url=uri
else /* got partial URI */ do
if port=80 then pp=''; else pp=':'port
url='http://'servername()pp'/'uri /* relocation */
end
/* Now set the response and build the response file */
if mtype=301 then
'RESPONSE HTTP/1.0 301 Moved Permanently' /* Set HTTP response line */
else
'RESPONSE HTTP/1.0 302 Moved Temporarily' /* Set HTTP response line */
'HEADER ADD Location:' url
call lineout tempfile, '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
call lineout tempfile, "<html><head><title>Moved</title></head>"
call lineout tempfile, "<body><h2>Document moved...</h2>"
call lineout tempfile, "<p>This document has moved"
call lineout tempfile, "<a href="""url""">here<a>."
call lineout tempfile, "</body></html>"
call lineout tempfile /* close */
return 'FILE ERASE TYPE text/html NAME' tempfile
/* ----------------------------------------------------------------------- */
/* Already completed (cache or prefilter. Record it and exit */
/* ----------------------------------------------------------------------- */
done_it: /* need globals, so call as routine */
parse arg status , amessage
issent=0
if datatype(status)<>'NUM' then status=0
if status=0 then issent=completed()
if (issent+status)=0 then return 0 /* otherwise exit */
say " Completed or redirected: " amessage
parse var sel action '?' awords
return 1
******************************************************************************/
/*-- DO_prefiltr: Call User Written pre-filter --**/
/******************************************************************************/
do_prefiltr: procedure expose source0 request0 seloriginal prefilter_name
parse arg amessage
signal on syntax name bad2a
signal on error name bad2a
yow='foo3='||prefilter_name||'(source0,request0,seloriginal,amessage)'
interpret yow
/* foo3=prefiltr(source0,request0,seloriginal,amessage)*/
signal not2a
end
bad2a:
signal off syntax
signal off error
say " Error in pre-filter routine " amessage
not2a: nop
signal off syntax
signal off error
return foo3
/* ----------------------------------------------------------------------- */
/* SEND_NON_DATADIR -- !TRANSFER -- Send a file not in the data directory */
/* This is basically obsolete, but is retained for occassional use */
/* ----------------------------------------------------------------------- */
send_non_datadir: procedure expose tempfile source0 seloriginal request0 request_ids
parse arg action , aword
taction=translate(action)
if abbrev(taction,'!TRANSFER=')=1 then
action=delstr(action,1,10)
else
if abbrev(taction,'!TRANSFER')=1 then
action=delstr(action,1,9)
do mm=1 to words(request_ids)
action=sref_replacestrg(action,word(request_ids,mm),aword)
end
a=stream(translate(action,'\','/'),'c','query exists')
if a="" then do
return not_found_response(seloriginal,' File not found ',' ')
end
file2=translate(action,'/','\')
return 'FILE TYPE ' sref_mediatype(a) ' nocache NAME' file2
/* ----------------------------------------------------------------------- */
/* -- Convert an answer, in list, into 1 */
/* ----------------------------------------------------------------------- */
is_true:procedure
parse upper arg ans,anslist
if wordpos(ans,anslist)>0 then
return 1
else
return 0
/* ----------------------------------------------------------------------- */
/* SHOWVARS: Show values of listed variables */
/* ----------------------------------------------------------------------- */
show_vars:
parse arg alist
call lineout tempfile, '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
call lineout tempfile, "<html><head><title> SRE-FILTER variables </title>"
call lineout tempfile, "</head>"
call lineout tempfile, "<body><h2>Values of selected variables</h2>"
call lineout tempfile,' <pre>'
do forever
parse var alist repargn '&' alist
putme='n.a.'
if repargn<>"" then
if symbol(repargn)="VAR" then
putme= value(repargn)
call lineout tempfile, repargn ' = ' putme
if alist="" then leave
end
call lineout tempfile,' </pre>'
call lineout tempfile, ' </body> </html> '
call lineout tempfile
return ' FILE ERASE TYPE text/html NAME ' tempfile
/* ----------------------------------------------------------------------- */
/* Write response headers, expects an HTML file */
/* ----------------------------------------------------------------------- */
do_auto_header: procedure
parse arg file , notfile
if notfile=0 then
stuff=sref_grab_file(file,30)
else
stuff=file /* if ssi's happened, already read file in */
if stuff=0 | stuff="" then return 0
dowrite=0
do until stuff=""
parse var stuff p1 '<' tag '>' stuff
if translate(word(tag,1))="HEAD" then do /* now in head !*/
dowrite=1
iterate
end
if dowrite=0 then iterate /* wait till we get into head .. */
if translate(word(tag,1))="/HEAD" then /* out of head, all done ! */
leave
/* is it a LINK or an META HTTP-EQUIV ? */
if translate(word(tag,1))="LINK" then do
isme=""
do mm=2 to words(tag)
isme=isme||word(tag,mm)
IF MM<words(tag) then isme=isme||" ; "
end
'HEADER ADD WWW-Link: '||isme
iterate
end
if translate(word(tag,1))="META" then do
parse var tag ameta atype '=' rest
if translate(atype)="HTTP-EQUIV" then do
parse var rest aval1 rest
aval1=strip(aval1) ;
aval1=strip(aval1,,'"')
aval2=" "
foo1=pos('CONTENT=',translate(rest))
if foo1>0 then do
aval2=substr(rest,9) ; aval2=strip(aval2)
aval2=strip(aval2,'b','"')
end
foo1=aval1||': '||aval2
'HEADER ADD '||foo1
iterate
end
end
end
return 0
/* ------------------------- End of generic response stuff --------- */
/*************************************************************************/
/*************************************************************************/
/* -------------------------------Server side include routines -------------- */
/* ----------------------------------------------------------------------- */
/* set up optlist -- the list of OPTIONS */
/* ----------------------------------------------------------------------- */
make_optlist: procedure expose optlist. no_include
parse arg selinfo
if no_include=1 then do
return 0
end
eek=0
if selinfo<>0 then do /* note we convert url's to regular ascii */
selinfo=translate(selinfo, ' ', '+'||'090a0d'x) /* Whitespace, etc. */
selinfo=packur(selinfo)
do until selinfo = " "
eek=eek + 1
parse var selinfo optlist.eek '&' selinfo
end
end
return eek
/* ----------------------------------------------------------------------- */
/* Main routine for doing server side includes (ssi's)
. We first read the candidate file into a big array.
. Then check for existence of include types.
. If none (or no_include=yes), then return 0 -- which means
. "go ahead and return the default document "
.
. Otherwise, add headers.n right after the the (first) <BODY> entry.
. add footers.n to the portion right BEFORE the (last) </BODY> entry.
. and
see if these are legit keyphrases, and process them..
,
, When no more candidates, return a 1.
, Note that 1 means "I've returned the results as a VAR, so we're done.
. (I could use an EXIT here, but i let the main routine exit for me )
.
*/
/* ----------------------------------------------------------------------- */
do_includes: /* many globals, so call as routine */
parse arg tfile
crlf='0d0a'x
if no_include=1 then /* don't bother, just return original file */
return 0
bigin=sref_grab_file(tfile,30)
if bigin=0 | bigin="" then do /*problem opening ! */
say " Could not open file for includes : " tfile
return 0 /* just send back file as is */
end
if c2d(right(bigin,1))=26 then
bigin=left(bigin,length(bigin)-1)
tbigin=translate(bigin)
inctype.1="INTERPRET"
inctype.2="OPTION"
inctype.3="INCLUDE"
inctype.4="REPLACE"
inctype.5="SELECT"
i5=5
if cgi_bin_dir<>0 then do
inctype.6='#'
i5=6
end
do mm=1 to i5
j.mm=pos(inctype.mm,tbigin)
end
apre=1 ; apost=1
if symbol('headers.1')<>"VAR" | headers.1=0 then apre=0
if symbol('footers.1')<>"VAR" | footers.1=0 then apost=0
/* note that j.i may or may not be in a keyphrase! */
booger=apre+apost; do iik=1 to i5 ; booger=booger+j.iik ; end
/* NO include keyphrases -- so send it (note we could use a VAR with bigin,
but this complicates caching and generates an expires inappropriately) */
if booger = 0 then do
return 0
end
/* add pre or post blocks if they have been specified (typically in initfilt) */
if apre=1 then do
m=1
preadd=""
do until symbol('headers.m')<>"VAR"
if headers.m="" then leave
preadd=preadd||headers.m||crlf
m=m+1
end
bigin=sref_insert_block(bigin,'BODY',preadd,1,'<','>')
end
if apost=1 then do
m=1
postadd=""
do until symbol('footers.m')<>"VAR"
if footers.m="" then leave
postadd=postadd||footers.m||crlf
m=m+1
end
bigin=sref_insert_block(bigin,'/BODY',pOSTadd,0,'<','>')
end
/* check for "send in pieces " -- requires No retain_bad_keyphrase,
no fix_expire, no 2nd delimiter, and no auto_header */
send_piece=0
if symbol('delim_1.2')<>"VAR" | symbol('delim_2.2')<>"VAR" | ,
delim_1.2=0 | delim_1.2="" then do
if retain_bad_keyphrases=0 & auto_header<>"ALWAYS" & fix_expire=0 then do
send_piece=1
'SEND TYPE text/html as ' file
end
end
/* now start processing bigin */
nsubs=0 /* # of substitutions encountered */
totinc=0 ; badints=0 ; goodints=0
/* For flexibility, we process this for each set of "keyphrase delimiters",
where set k=1..K is defined using delim_1.k and delim_2.k (typically set
in initfilt) */
ithdelim=1
adelim1=delim_1.ithdelim
adelim2=delim_2.ithdelim
if adelim1="" | adelim2="" then return 0 /* bad initial delimiter -- don't do includes */
one_more_scan: /* jump here for multiple delimiters sets */
outbig=''
do forever /* done when done */
parse var bigin t1 (adelim1) in1 (adelim2) bigin
if send_piece=1 & t1<>"" then do
'VAR NAME T1 '
end
else do
outbig=outbig||t1
end
if in1="" & bigin="" then leave
/* see if in1 is one of the inctypes (inctypes are aka keywords)*/
in2=translate(in1)
in2=translate(in2,' ','=:;') /* space is the generic seperater */
aninc=0 ; more2=0
/* 2 words, is first one of our keywords ? */
do mm=1 to i5
if abbrev(word(in2,1),inctype.mm)=1 then do /* it's the mm'th inc type */
aninc=mm
if mm>1 & mm<5 then do /* not interpret or select */
thearg=word(in2,2)
end
else do
if mm=1 then do
foo=pos('INTERPRET',translate(in1))
thearg=substr(in1,foo+9)
more2=1
end
if mm=6 then do
foo=pos('#',translate(in1))
thearg=substr(in1,foo+1)
more2=1
end
if mm=5 then do
foo=pos('SELECT',translate(in1))
thearg=substr(in1,foo+6)
more2=1
end
end
leave /* leave this lttle loop */
end
end
/* Not a keyword, or a syntactically bad keyphrase */
if aninc=0 | (aninc>0 & words(in2)<>2 & more2=0 & retain_bad_keyphrases=1) then do
if bigin<>"" then
t1=adelim1||in1||adelim2 /* leave it be */
else
t1=adelim1||in1 /* openended "comment" */
if send_piece=1 then do
'VAR NAME T1'
end
else
outbig=outbig||t1
iterate
end
/* if here, we have a (possibly) good keyphrase */
nsubs=nsubs+1
select
when inctype.aninc='#' then do
putme=do_cgi_include(thearg)
end
when inctype.aninc="INTERPRET" then do
putme=line_interpret(thearg)
if putme="" then
badints=badints+1
else
goodints=goodints+1
end
when inctype.aninc="OPTION" then do
putme=line_message(thearg)
end
when inctype.aninc="INCLUDE" then do
putme=line_include(thearg)
if putme<>"" then
totinc=length(putme)+totinc
end
when inctype.aninc="REPLACE" then do
putme=line_replace(thearg)
end
when inctype.aninc="SELECT" then do
useit=do_select(thearg)
putme=""
if useit=0 then do /* 0=EXCLUDE it ! */
/* scan for next SELECT, and delete everything in between */
bigin=bigin
putme=""
do until bigin=""
parse var bigin tt1 (adelim1) tt2 (adelim2) bigin
if TT2="" & bigin="" then
leave
IF translate(word(tt2,1))="SELECT" then
leave
end
end /* useit */
end /* select */
otherwise
putme=""
end
/* strip trailing ctl-z ? */
if c2d(right(putme,1))=26 then
putme=left(putme,length(putme)-1)
bigin=putme||bigin /* this is the recursive part */
end /*of bigin parse loop */
if totinc>0 | (goodints+badints)>0 then
say " Includes: " totinc " ; (good/bad interps ) " goodints "," badints
/* if send mode, then all done */
if send_piece=1 then do
return 'SEND COMPLETE '
end
/* Not "send in pieces, so check to
do it again for another set of delimiters ? */
ithdelim=ithdelim+1
if symbol('delim_1.ithdelim')="VAR" & symbol('delim_2.ithdelim')="VAR" then do
if delim_1.ithdelm<>"" & delim_2.ithdelm<>"" then do
adelim1=delim_1.ithdelim
adelim2=delim_2.ithdelim
if pos(adelim1,outbig)>0 then do
bigin=outbig
signal one_more_scan
end
end
end
/* else, we are done */
if c2d(right(outbig,1))=26 then
outbig=left(outbig,length(outbig)-1)
if fix_expire>0 then do /* override goserve response headers */
foo=sref_expire_response(fix_expire,length(outbig))
end
if auto_header="ALWAYS" then do
foo=do_auto_header(OUTbig,1)
end
'var type text/html as ' tfile 'NAME outbig ' /* tell goserve to send it */
return 1
/* ----------------------------------------------------------- */
Evaluate a cgi-bin (NSCA HTTPD style server side include */
/* ----------------------------------------------------------- */
do_cgi_include:
parse arg thearg
parse var thearg atype aval
/* valid atypes:
INCLUDE = Include a file
ECHO = "replace" with a cgi-bin variable
FSIZE= Size of a file
FLASTMOD = Last modification date of a file
EXEC = Execute a command file or a cgi-program
*/
tatype=translate(atype)
if tatype="INCLUDE" then do
parse var aval foo '=' aval
aval=strip(strip(aval),,'"')
putme=line_include(aval)
return putme
end
if tatype="FSIZE" | tatype="FLASTMOD" then do
parse var aval foo '=' aval
aval=strip(strip(aval),,'"')
afile=line_include(aval,'YES')
drop stuff
oy=sysfiletree(afile,stuff,'F')
if stuff.0=0 then
return cgi_inc_errmsg
parse var stuff.1 adate atime asize .
if tatype="FLASTMOD" then do
putme=sref_replacestrg(adate,'-','/','ALL')
if cgi_inc_timefmt="ALL" then putme=putme||' '||atime
return putme
end
if tatype="FSIZE" then do
if translate(cgi_inc_sizefmt)="ABBREV" then do
if asize>=1000000 then
return format(asize/1000000,,2)||'M'
if asize>=1000 then
return format(asize/1000,,2)||'K'
end
return asize /* not abbrev, or < 1000 */
end
end
if tatype="CONFIG" then do
parse var aval t1 '=' t2 ;t1=translate(t1)
t2a=strip(strip(t2),,'"')
putme=""
select
when t1="ERRMSG" then
cgi_inc_errmsg=t2a
when t1="TIMEFMT" then /* non standard for now */
cgi_inc_timefmt=translate(t2a)
when t1="SIZEFMT" then
cgi_inc_sizefmt=translate(t2a)
otherwise
putme=cgi_inc_errmsg
end
return putme
end
if tatype="ECHO" then do
parse var aval foo '=' findme ; findme=translate(findme)
findme=strip(strip(findme),,'"')
select
when findme="DOCUMENT_NAME" then
putme=tfile
when findme="DOCUMENT_URI" then
putme= seloriginal
when findme="DATE_LOCAL" then do
putme=date()
if cgi_inc_timefmt="ALL" then
putme=putme||' '||time()
end
when findme="DATE_GMT" then do
putme=date()
if cgi_inc_timefmt="ALL" then
putme=putme||' '||line_replace("TIME_GMT")
end
when findme="LAST_MODIFIED" then do
putme=line_replace("CREATION_DATE")
if cgi_inc_timefmt="ALL" then
putme=putme||' '||line_replace("CREATION_TIME")
end
when findme="SERVER_SOFTWARE" then
putme=server('H')||' '||filter_name
when findme="SERVER_NAME" then
putme=servername
when findme="GATEWAY_INTERFACE" then
putme="CGI/1.1"
when findme="SERVER_PROTOCOL" then
putme=protocol
when findme="SERVER_PORT" Then
putme=serverport
when findme="REQUEST_METHOD" then
putme=verb
when findme="PATH_INFO" then
putme=" Path_info n.a. "
when findme="PATH_TRANSLATED" Then
putme="Path_translated n.a. "
when findme="SCRIPT_NAME" then
putme=action
when findme="QUERY_STRING" then
putme=awords
when findme="REMOTE_HOST" then
putme=line_replace("USERNAME")
when findme="REMOTE_ADDR" then
putme=who
when findme="AUTH_TYPE" then
putme="Basic Access Authentication Scheme"
when findme="AUTH_NAME" then do
afield=reqfield('Authorization')
parse var afield . m64 . /* get the encoded cookie */
dec=pack64(m64) /* and decode it */
parse upper var dec putme ':' .
end
when findme="REMOTE_IDENT" then
putme="Remoted_ident n.a. "
when findme="CONTENT_TYPE" then
putme="Content_type n.a."
when findme="CONTENT_LENGTH" then
putme="Content_length n.a."
when abbrev(findme,"HTTP_") then do
if findme="HTTP_ACCEPT" then do
i = 1
_acc = REQFIELD("accept")
acc = '%'
ClientAccepts = ''
do while (acc \= _acc)
acc = REQFIELD("accept", i)
if (ClientAccepts \= '') then ClientAccepts = ClientAccepts', 'acc
else ClientAccepts = acc
i = i+1
end
putme=clientaccepts
end
else do
parse var findme . '_' findme2
putme=reqfield(findme2)
end
end
otherwise
putme=cgi_inc_errmsg
end /* select */
return putme
end
if tatype="EXEC" then do
parse var aval foo '=' aproc
aproc=strip(strip(aproc),,'"')
putme=line_interpret('FILE '||aproc)
return putme
end
return cgi_inc_errmsg
/* ----------------------------------------------------------- */
/* Evaluate thearg in SELECT keyphrase */
/* ----------------------------------------------------------- */
do_select:
parse arg thearg
select.result=1
select.results=-135 /* an arbitrary non 0 / 1 value */
if translate(thearg)="END" then /* if thearg="END", it's junk */
return 1
thearg=translate(thearg, ' ', '090a0d'x)
/* interpret thearg */
signal on syntax name sele1
signal on error name sele1
interpret thearg
signal off syntax
signal off error
if select.results <> -135 then /* aid to sloppy programmers */
select.result=select.results
signal sele2
sele1: /* here on syntax error */
signal off syntax
signal off error
foo=condition('d')
say " Error in SELECT: " foo
audit "Error in SELECT: " thearg " : " foo
return 1
sele2:
if select.result=1 then return 1
return 0
/* ----------------------------------------------------------------------- */
/* Look for INTERPRET keyphrases
. note use of interp_data created above
. Note: Results of these INTERPRET keyphrases, for inclusion in the document,
. must be stored in the INTERPRET.RESULTS variable.
.
. There are 3 types of code-blocks:
. 1) Included in the keyphrase: INTERPET CODE rexx code
. 2) Included in the INTERPET.IN collection of code-blocks: INTERPRET ALABEL
. 3) In it's own file: INTERPET FILE FILENAME.
*/
/* ----------------------------------------------------------------------- */
line_interpret:
parse arg thisarg
/* pull block from: { thisarg } block {nextarg } */
thisarg=translate(thisarg, ' ','090a0d'x) /* Whitespace, etc. */
thisarg=strip(thisarg)
atype=strip(translate(word(thisarg,1)))
select
when atype='FILE' then do /*file match */
bfile=strip(word(thisarg,2))
bfile=do_virtual_file(servdir,bfile)
if bfile<>0 then do
j0=fileread(bfile,'tmp1')
thestring=""
do j1=1 to j0
j2=strip(translate(tmp1.j1,' ','000d0a001a'x))
j3=right(j2,1)
select
when j3=";" then
thestring=thestring||j2||crlf
when j3="," then
thestring=thestring||left(j2,length(j2)-1)
otherwise
thestring=thestring||j2||" ; "||crlf
end
end
end
else
thestring=0
end
when atype="CODE" then do
thestring= strip(subword(thisarg,2))
end
otherwise do
if interp_data=0 then
call get_interpret
thestring=strip(sref_extract_block(interp_data,thisarg))
end
end
if thestring="" | thestring=0 then do
say " Can not find INTERPRET keyphrase: " thisarg
audit " Can not find INTERPRET keyphrase: " thisarg
return ""
end
thestring=translate(thestring, ' ','090a0d'x) /* Whitespace, etc. */
interpret.results="" /* clear any residual value */
interpret.result="" /* for sloppy programmers.... */
signal on syntax name doggs ;signal on error name doggs
interpret thestring
signal off syntax ; signal off error
if interpret.results="" then /* help out forgetfull programmers */
interpret.results=interpret.result
return interpret.results
doggs: /* here on syntax error */
signal off syntax ; signal off error
foo=condition('d')
say " Error interpreting: " thisarg " : " foo
audit "Error interpeting " thisarg " : " foo
return ""
/* ----------------------------------------------------------------------- */
/* Look for INCLUDE keyphrases
. If present, pull in lines from INCLUDE file.
.
. Note: INCLUDE files are subject to further processing
. -- so BEWARE of recursive TRAPS !
. Justfile argument used by CGI_INCLUDE routine
*/
/* ----------------------------------------------------------------------- */
line_include:
parse arg incfil0 , justfile
justfile=translate(justfile)
/*---- INCLUDE files can be anywhere (this is not a security hole,
since requesters can not send "INCLUDE" keyphrases, they can
only request documents with INCLUDE keyphrases embbedded */
incfile=strip(translate(incfil0,'\','/')) /* just to be thorough */
dd0=strip(translate(dir,'\','/'))
/* if no : or first char is \, then it's a pathless file name-- assume it's in
data directory */
foo=pos(':',incfile)
foo1=left(incfile,1)
if foo=0 & foo1<>"\" then
incfile=dd0||incfile
if justfile="YES" then return(incfile)
putme=sref_grab_file(incfile,20)
if putme=0 then putme=""
return putme
end /* INCLUDE= */
/* ----------------------------------------------------------------------- */
/* This will do a OPTION replace on the line.
Keyphrases of the form <!--OPTION=nnn--> are looked for, the nnn is decoded,
and optlist.nnn is use to replace the keyphrase (if nnn not specified,
the keyphrase is removed).
Note that optlist is construced from elements following a ?xxx&xxx type of
request string (c
*/
/* ----------------------------------------------------------------------- */
line_message:
parse arg id1
putme=""
if datatype(id1)='NUM' then
if symbol('optlist.id1')='VAR' then /* check for a mess up */
putme=optlist.id1
return putme
/* ----------------------------------------------------------------------- */
/* This will do a REPLACE: on a line. The currently supported values are
DATE todays date
TIMEGMT current time (GMT)
TIME current time
CREATION A message on the creation date & time of the file
CREATION_DATE Just the creation date
CREATION_TIME Just the creation time (use with CREATION_DATE and your own message)
WEBMASTER The contents of the WEBMASTER parameter
REFERER The referer (from the request header)
BROWSER The requesters browser (from the request header)
USERNAME ABC.DDD.GOV type name, or ip address if n.a.
INHOUSE.n n = an integer. Used for messages to INHOUSE users only.
SUPERUSER.n n = an integer. Used for messages to Superusers only
HITS The nth hit for this file (requires looking at Counter_file
Also COUNTS, OPTION_HITS.n and OPTION_COUNT.n variants
WEBMASTER = Webmaster address
SERVERNAME = Name of server (i.e.; WWW.FOO.ORG)
SERVER = Server software (i.e. GOSERV 2.45)
FILTER_NAME = The name of this filter (set at the top of this file)
VARIABLE.varname = Extract value of varname. Examples include
SERVERPORT PRIVSET
USERNAME SEL etc.
READ_HEAD Do a READ HEADER VAR PUTME , append <PRE>
and ... Check replacestrg_file (static replacements) if no match from above.
/* ----------------------------------------------------------------------- */
*/
line_replace:
parse arg targ
parse upper var targ reparg "." repargn /* parse out VAR.j types of labels */
issuper=0 ; isin=0;
if wordpos('SUPERUSER',translate(privset))>0 then
issuper=1
else
if wordpos('INHOUSE',translate(privset))>0 then
isin=1
do while joe>0
select
when reparg="DATE" then
putme=date('N')
when reparg="TIME" | reparg="TIMELOCAL" then
putme=time('C')
when reparg="TIMEGMT" | reparg="TIME_GMT" then do
/* Computes GMT time as Wed, 12 Aug 1996 21:18:20 format */
fii=sref_new_gmt()
parse var fii eek ',' d1 d2 d3 t1
putme=t1||' GMT '
end
when reparg="DATEGMT" then do
fii=sref_new_gmt()
parse var fii eek ',' d1 d2 d3 .
putme=d1||' '||d2||' '||d3
end
when reparg="USERNAME" then do
if clientname0=0 then
putme=clientname()
else
putme=clientname0
end
when reparg="FILTER_NAME" | reparg="FILTER_NAME" then
putme=filter_name
when reparg="HOME_NAME" | reparg="HOMENAME" then
putme=home_name
when reparg="CREATION" then do /* a creation-date-time message */
eek=sysfiletree(translate(file,'\','/'),gosh,'F')
poop=gosh.1
parse var poop adate atime .
putme =' <em> This document last modified at '||atime||', on '||adate|| '. </em>'
end
when reparg="CREATION_DATE" then do /*just the creation date */
eek=sysfiletree(translate(file,'\','/'),gosh,'F')
poop=gosh.1
parse var poop adate atime .
putme =adate
end
when reparg="CREATION_TIME" then do /* just the creation time */
eek=sysfiletree(translate(file,'\','/'),gosh,'F')
poop=gosh.1
parse var poop adate atime .
putme =atime
end
when reparg="READ_HEAD" then do /* read/display the request header */
'READ HEADER VAR PUTME '
putme='<PRE>'||putme||'</pre>'
end
when (reparg="INHMESS" | reparg="INHOUSE" )& isin=1 then do
putme=inhouse.repargn
if symbol('putme')<>'VAR' then putme=" "
end
when (reparg="SUPMESS" | reparg="SUPERUSER") & issuper=1 then do
putme=superuser.repargn
if symbol('putme')<>'VAR' then putme=" "
end
when abbrev(reparg,"REFER")=1 then
putme=reqfield("Referer")
when (reparg="USER-AGENT" | reparg="BROWSER" ) THen
putme=reqfield("User-Agent")
when (reparg="URL") then do
putme=sref_fix_url(seloriginal,servername,serverport)
end
when pos("HIT",reparg)+pos("COUNT",reparg) > 0 then do
trymess=pos("OPTION",reparg)
dowordy=pos("HIT",reparg)
if dowordy>0 then dowordy=1
if trymess>0 & symbol('optlist.repargn')='VAR' then do
putme=optlist.repargn
if dowordy>0 then
putme=OPTION_hit_line||putme /* use a "message" argument */
end
else do /* get from file (or prior count */
putme=get_hit(action,dowordy)
end
end
when reparg="SERVERNAME" | reparg="SERVER_NAME" then /* servers ip name */
putme=servername /* servername set at top of filter */
when reparg="VARIABLE" then do /* get a variable defined in this filter program */
if repargn<>"" then
if symbol('repargn')="VAR" then
putme= value(repargn)
end
when reparg="SERVER" then
putme=server('H')
when reparg="WEBMASTER" then
putme=webmaster
otherwise /* see if in the replacement strings file*/
putme=chk_replaces(targ)
end /* select */
return putme /* return it */
/* ----------------------------------------------------------------------- */
/* Scan a replacement strings file for a match with repme.
. If found, return the string, else return blank
. Might first need to read in replacement strings file (or call it down
. from macrospace)
*/
/* ----------------------------------------------------------------------- */
chk_replaces:
parse arg repme
if replines.0<0 then
call setup_replines repme
if replines.0 =0 then return " " /* no replacement file */
do nn=1 to replines.0
if repme=replines.nn.label then
return replines.nn.value
end /* do */
return ''
/* ----------------------------------------------------------------------- */
/* GET_HIT: (used by REPLACE HIT and REPLACE HIT_MESS keyphrases)
. look in counter_file for # of hits (augment count or add entry),
. Returns a string with containing # of hits.
. If make_wordy_flag, uses the message in the counter_file to
. make a "wordy" string.
. Note that current_hit stem variable is saved/used (if this
. is not the first request for this "anaction", we use results
. stored in current_hit.xx)
. usage: astring=get_hit(anaction,make_wordy_flag)
.
*/
/* ----------------------------------------------------------------------- */
get_hit: procedure expose counter_file current_hit.
parse arg taction , dowordy
/* did we already find out which hit this is ? */
if translate(current_hit.item)=taction then do /* Must know current_hit */
anum=current_hit.num
use1=current_hit.mess1
use2=current_hit.mess2
if dowordy>0 then
putme=use1||" "||anum||" "||use2 ;
else
putme=anum
return putme
end
/* look for it */
if counter_file=0 then return "" /* but only if we have a file */
stuff=sref_lookup_count(counter_file,taction,'ADD',0,0)
parse var stuff status ict use1 ',' use2
if status=0 then return "" /* no file, so no count string */
/* now put into a string if dowordy>0 */
if dowordy=0 then
putme=ict
else do
if use1=" " & use2=" " then
use1=" # of hits = "
else do
use1=STRIP(use1) ; use2= STRIP(use2)
end
putme=use1||" "||ict||" "||use2
end
current_hit.num=ict /* save current hit */
current_hit.item=taction
current_hit.mess1=use1
current_hit.mess2=use2
return putme
/* ---------------------------End of ssi routines----------------------------*/
/**************************************************************************/
/*************************************************************************/
/* ----------------------Miscellanoue routines---------------------------*/
/* -----------------------------------------------------------------------*/
/* Write a 'busy server' response file */
/* -----------------------------------------------------------------------*/
busy_server: procedure
parse arg tempfile, action
asn=servername()
amessx='Server action need to process "'sel'", but the server is busy. '
return 0
/* ----------------------------------------------------------------------- */
/* ALIAS_ACTION: look in alias_file for "action" aliases
. usage: newaciton=alias_action(anaction)
. Wildcard matching is supported.
. The basic idea is:
. given a candiate STRING
. look for a matching TARGET, and if one is found
. use the REPLACE_STRING as a (partial) substitute for the STRING.
. The alias_file is structured as:
. Target*xx replace_string*xx (the *'s and xx are optional)
.
. The wildcard matching procedure is used extensively
.
. Note that replace_string can take some special values:
. !TRANSFER - Transfer a file from any directory (not just a data directory)
. !MOVED http://xxxxx - Return a "moved" response to the http://xxx url.
.
*/
/* ----------------------------------------------------------------------- */
alias_action: procedure EXPOSE ALIAS_file macrospace_input
parse arg sel0
call file_or_macro "ALIASES"
tsel=translate(sel0)
gotit=0 ; doexact=0
do m=1 to 100000 /* look in public_files list */
if symbol('filelines.m')<>"VAR" then leave
ares=sref_wildcard(tsel,filelines.m,doexact)
parse var ares astat "," aurl ; astat=strip(astat)
if astat=0 then iterate /* no match */
aurl=strip(aurl)
gotit=m
if aurl="" then do
usesel=tsel
end
else do
usesel=aurl
end
if ares=1 then
leave /*first exact match rules */
else
doexact=1
end
if gotit>0 then /* if gotit, then reset the sel */
return gotit ' ' usesel
else
return 0 " " sel0
/* --------------------------MESSAGE BOX UTILITIES ---------------------------------*/
/* The following is a set of message box utilities. If the exposes were
checked, it could be placed in a seperate file
*/
/* ----------------------------------------------------------------------- */
/* ----------------------------------------------------------------------- */
/* create a form to select a message box */
/* ----------------------------------------------------------------------- */
what_messbox: procedure expose tempfile messbox_dir
parse arg header
if header=" " then header="Message Boxes"
call lineout tempfile, '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
call lineout tempfile, "<html><head><title>Message Box Selection</title></head>"
ee=messbox_dir||"\*.LOG"
eej=sysfiletree(ee,gotlog,'F')
/* no messages box files? */
if gotlog.0=0 then do
call lineout tempfile,' <STRONG> No messages boxes exist ! </strong> '
call lineout tempfile,'<hr> <a href="/" rel="Parent"> Return to home page </a> '
call lineout tempfile,'</body> </html> '
call lineout tempfile
return 0
end
header=translate(header, ' ', '+'||'090a0d'x) /* Whitespace, etc. */
header=packur(header)
/* else, create a form with message boxes listed therein */
call lineout tempfile, '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
call lineout tempfile, "<body><h2>" header "</h2>"
call lineout tempfile,' Please view .. ?'
call lineout tempfile,' <FORM ACTION="!viewmess" METHOD="GET"> '
call lineout tempfile,' <SELECT NAME="messbox" SIZE=5> '
do mm=1 to gotlog.0
aff=filespec('name',gotlog.mm)
parse var aff affname "." .
affname=translate(affname)
al2='<OPTION value="'||affname||'" >Messages to '||affname
call lineout tempfile,al2
end
call lineout tempfile,'</SELECT>'
call lineout tempfile,'<INPUT TYPE="submit" VALUE="Get Messages">'
call lineout tempfile,'</FORM> <hr>'
call lineout tempfile,'<a href="/" rel="Parent"> Return to home page </a>'
call lineout tempfile,'</body> </html>'
call lineout tempfile
return 1
/* ----------------------------------------------------------------------- */
/* Set up a return string to look at the messages in desired message box */
/* ----------------------------------------------------------------------- */
viewmessage: procedure expose messbox_dir tempfile
parse arg list
parse var list atype '=' abox
t1=messbox_dir||"/"||abox||".LOG"
ause=stream(t1,'c','query exists')
if ause="" then do
call lineout tempfile, '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
call lineout tempfile, "<html><head><title>Messages Can Not Be Viewed</title></head>"
call lineout tempfile, "<body>"
call lineout tempfile, "<h2>Could not view message box </h2>"
call lineout tempfile, " Could not find the " abox " message box. <hr>"
call lineout tempfile,'<a href="/" rel="Parent"> Return to home page </a>'
call lineout tempfile, "<hr></body></html>"
call lineout tempfile
eek='FILE ERASE TYPE text/html NAME '||tempfile
end
else
eek= 'FILE TYPE text/plain nocache NAME ' t1
return eek
/* --------------------------End of message box utilities------------------- */
/******************************************************************************/
/* ------------------------------------------------------------------------ */
/* DO_VIRTUAL_FILE: Convert from URL to file name-- add virtual directory of data directory.
ddir : data directory
sel : request string (after sre-filter has modified it)
virtual_dir : List of virtual file replacement possiblities.
virtual_dir.label.nv virtual_dir.dir.nv virtual_dir.subok
Return: file name (with drive/directory) or 0 if not found/not allowed
*/
DO_VIRTUAL_FILE:PROCEDURE Expose virtual_file macrospace_input
parse arg ddir,action,nocheck
signal on error name novirt1 ; signal on syntax name novirt1 /* in case macro proc n.a.*/
virtok=0
ado=SREF_DO_VIRTUAL(ddir,action , macrospace_input ,virtual_file)
virtok=1
novirt1:
signal off syntax ; signal off error
if virtok=0 then say " problem with virtual file lookup "
if abbrev(translate(ado),'HTTP:\\')=1 then do
ado=translate(ado,'/','\')
say " Move: " ado
parse var ado aurl . /* parse out junk */
is301=301
FOO=moved(aurl,is301)
FOO
return "!MOVED"
end
if virtok=0 | ado=0 then do
ddir=translate(ddir,'\','/')
ddir=strip(ddir,'t','\')
ado=translate(ddir||'\'||action,'\','/')
end
if nocheck<>1 then do
ado=stream(ado,'c','query exists')
if ado="" then ado=0
end
return ado
/***************************************************************************/
/* ---------- Start of access control (logon) procedures section ----------*/
/*
do_logon : Main logon routine for requests. Will send out an authorization
response if necessay
Calls loguser
Ispriv(target) : Determines if requester has the target privilege. If not,
check user_file, or do an authorization. Call loguser
loguser: Checks if a inhouseips. If not, sees for verification field. If there,
compares agains user_file. If not, tells do_logon or ispriv to ask for
authorization
Goodips and badips: checks agains inhouseips and disallowedips stem variables.
Also pulls inhouseips. specific privset out.
*/
/* -----------------------------------------------------------------------*/
/* LOGON checking
. This basically calls loguser, and then either let's the requester
. back into the main filter, or asks for another username/password attempt.
. Thus, we only return from this with a sucess -- or we go back to
. GOSERVE with a "send authorization" response.
*/
/* -----------------------------------------------------------------------*/
do_logon: procedure expose inhouseips. inhouse user_file inhouse_privs source0 request0 seloriginal clientname0 macrospace_input
parse arg anip , howtough
/* Check if legit user. */
logtest = loguser(anip)
parse var logtest isallowed username privset
/* not allowed? -- ask user for logn info (maybe for the nth time! */
if isallowed= 0 then do
'header add WWW-Authenticate: Basic Realm=<'|| inhouse|| '>' /* challenge */
exit response('unauth', "for realm " inhouse " was not authorized")
end
/* NOTE: after exiting to goserv, goserv will recall this filter when answer sent
back by requester. */
/* if here, allowed -- accept we might check for INHOUSE only in caller! */
return username privset
/* -----------------------------------------------------------------------*/
/* Check privileges. Note that priviliges are met with privset
. This may require a logon.
. This procedure is often used for "secondary" logons
. (i.e.; to ascertain mail, server reset, and similar privileges) */
/* -----------------------------------------------------------------------*/
ispriv: procedure expose tempfile inhouse inhouseips. user_file privset inhouse_privs clientname0 macrospace_input
parse arg targclass
if wordpos("SUPERUSER",privset)>0 then return 1 /* superuser has all privs */
if wordpos(targclass,privset)>0 then return 1 /* may already have been set*/
/* if not already got this privilege
Force user to logon (auto logon not sufficient, need to check privs) */
logtest = loguser("0.0.0.0")
parse var logtest isallowed username privset
/* if noauth found, try a www-authorize request. We will end up back in
this routine after client responds (and will have a privset) to check*/
if username="NOAUTH" then do
say " Need privs, so user has to logon "
'header add WWW-Authenticate: Basic Realm=<'|| inhouse|| '>' /* challenge */
exit response('unauth', "for realm " inhouse " was not authorized")
end
if targclass=" " then return privset /* ' ' means return it, no checking*/
/* if here, got some kind of logon stuff, which did not match privset */
/* See if got the right privilige */
FOO2=WORDPOS(TARGCLASS,PRIVSET)
if foo2 > 0 then return 1 /* he has this privilege */
/* might be a non-owner superuser? */
if wordpos("SUPERUSER",privset)>0 then return 1
/* if here, does not have this privilige */
foo=not_allowed_message(username,isallowed)
return 0
/* -----------------------------------------------------------------------*/
/* Function to check on whether this guy is legitimate or not.
. If not from inhouse domains (set in inhouseips.) then hit him up for
. username/password authorization (and then perhaps tell GOSERVE to ask for it)
.
. Returns isallowed username privs :
. isallowed: 1 if okay, 0 if failed
. username : if inhouse, then xxx from xxx.yyy.etc, else IP address
. privs : special privileges (might be several)
NOTE: Superuser should NOT go through this (SUPERUSERS are not detected here
*/
/* -----------------------------------------------------------------------*/
loguser:procedure expose inhouseips. inhouse user_file crlf inhouse_privs ,
clientname0 macrospace_input
parse arg whome /* the fellows ip address */
crlf='0d0a'x
if whome='0.0.0.0' then signal trylog /* explicitly do not check inhouse*/
call goodips(whome) /* is this an inhouse connect */
isinhouse=result
if isinhouse=0 then signal trylog
/* if here, inhouse'ers are let off easy.. */
if clientname0=0 then
myname=clientname()
else
myname=clientname0
parse var myname myname1 "."
return 1 myname1 inhouse_Privs||' '||privset1
/* jump here to allow user to attempt a logon */
trylog:
afield=reqfield('Authorization') /* see if incoming authorization available */
/* if no authorization found, then let calling routine know
(so a www-auth can be done */
if afield=" " then
return 0 NOAUTH 0 /* no auth, maybe ask?*/
/* otherwise, we got an authorization (probably due to a prior request
that came here). So... check his username/password */
parse var afield . m64 . /* get the encoded cookie */
dec=pack64(m64) /* and decode it */
parse upper var dec user ':' pwd /* split to userid and password */
/* Now see if user and pw are on the user_file list:
. user pwd class privset (first user)
. Note that privset can be many words
. (in a sense, class is just the first of the privset !
*/
call file_or_macro "USER"
nlines=filelines.0
if nlines=0 then do
say " no User file "
return 0 NOFILE 0
end
/* scan users file */
do mm=1 to nlines
parse upper var filelines.mm auser apwd privset
if (auser = user | auser='*') & (apwd = pwd | apwd='*') then do
return 1 auser privset
end
end
/* if here, no such user */
return 0 NOTUSER 0
/* -----------------------------------------------------------------------*/
/* see if matches one of a set of bad ips (1 if yes)*/
/* -----------------------------------------------------------------------*/
badips:
parse arg anips
parse var anips ip.1 '.' ip.2 '.' ip.3 '.' ip.4
mm=0
do forever
mm=mm+1
if symbol('unallowedips.mm')<>"VAR" then return 0
parse var unallowedips.mm uip.1 '.' uip.2 '.' uip.3 '.' uip.4
match=1
do mm2=1 to 4
if uip.mm2="*" then iterate
if uip.mm2=ip.mm2 then iterate
match=0 /*if here, not a match */
leave
end
if match=1 then return 1 /* got a match */
end
/* -----------------------------------------------------------------------*/
/* see if matches one of a set of good ips (1 if yes)*/
/* -----------------------------------------------------------------------*/
goodips: procedure expose inhouseips. privset1
parse arg anips
parse var anips ip.1 '.' ip.2 '.' ip.3 '.' ip.4
mm=0 ; ndo=0
do mm=1 to 10000 /*how many inhouseips. are there */
if symbol('inhouseips.mm')<>"VAR" then leave
if inhouseips.mm=" " then leave
if inhouseips.mm=0 then leave
ndo=ndo+1
end
if ndo=0 then return 0
do m2=1 to ndo /* exact matches first */
parse var inhouseips.m2 uip.1 '.' uip.2 '.' uip.3 '.' uip.4 privset1
match=0
do mm2=1 to 4
if ip.mm2=uip.mm2 then
match=match+1
end
if match=4 then
return 1 /*got an exact match */
end
match=1
do m2=1 to ndo /* wild card matches */
match=1
parse var inhouseips.m2 uip.1 '.' uip.2 '.' uip.3 '.' uip.4 privset1
do mm2 =1 to 4
if uip.mm2="*" then iterate
if uip.mm2=ip.mm2 then iterate
match=0 /*if here, not a match */
leave
end
if match=1 then return 1
end
return 0 /* no match */
/* --------------------------------------- */
/* Generate a "you can't have this "message */
/* if here, does not have this privilige */
not_allowed_message: procedure expose tempfile
parse arg username, isauser
call lineout tempfile, '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
call lineout tempfile, "<html><head><title>Not privileged user</title>"
call lineout tempfile, "</head>"
call lineout tempfile, "<body><h2>Not a privileged user!</h2>"
if isauser=0 then
call lineout tempfile, "<p> You do NOT have logon rights "
else
call lineout tempfile, "<p>" username " is not given access to this URL "
call lineout tempfile, "</body></html>"
call lineout tempfile /* close */
return 0
/*------------*/
/* Access control routine */
sref_allow_access:procedure expose macrospace_input ssi_allow ssp_allow
parse upper arg sel, allow_access, access_file , privset
/* superusers are home free */
/* if wordpos("SUPERUSER",translate(privset)) > 0 then
return 10000*/
/* no access controls */
if allow_access="YES" & ssi_allow=1 & ssp_allow=1 then /* everyone gets */
return 10000
/* inhouse is given access if ssi and ssp are allowed.*/
if allow_access="INHOUSE" then /* inhouse and superusers aren't hassled*/
if wordpos("INHOUSE",translate(privset)) > 0 then
if ssi_allow=1 & ssp_allow=1 then
return 10000
/* if here, check file specific privileges (any access, ssi and ssp privs) */
call file_or_macro 'ACCESS'
/* first successful wildcard match rules! */
sel=translate(sel,'/','\')
do im=1 to filelines.0
amatch=0
parse upper var filelines.im alabel0 aprivs "," ssissp
alabel0=translate(alabel0,'/','\')
ares=sref_wildcard(sel,alabel0)
parse var ares amatch ',' alabel
if amatch>0 then do /* match -- check privs */
select
when wordpos('NO',aprivs)>0 then do
nop
end
when aprivs="" | wordpos('*',aprivs)>0 | wordpos('YES',aprivs)>0 then do
say " All access permited #: " im ssissp
return ssissp /* open to all */
end
otherwise do
do mm=1 to words(aprivs)
if wordpos(word(aprivs,mm),privset)>0 then do
say " Access permited #: " im ssissp
return ssissp
end
end / *mm */
end / * otherwise */
end /* selec t? */
end /* check */
end /* filelines */
/* inhouse is given access (and by default, ssi and sspl */
if allow_access="INHOUSE" then do /* inhouse and superusers aren't hassled*/
if wordpos("INHOUSE",translate(privset)) > 0 then
return 10000
end
/* if here, not superuser or inhouse, and no match -- hence, no access */
say " Match failed against: " filelines.im
return 0
/* -----------------------------------------------------------------------*/
/* check for a "range request". If none found, then return 0.
Else, return an appropriate VAR response, or if multiple
sends, a 1
For details on range retrieval, see draft-ieft-http-range-retrieval-00.txt
(try ds.internic.net)
*/
/* -----------------------------------------------------------------------*/
process_range: procedure expose outbig
parse arg afile,atype
ranges=reqfield('range:')
if ranges="" then do
'HEADER ADD Accept-Ranges: bytes '
return 0
end
/* else, found a byte acceptance range */
parse upper var ranges foo1 'BYTES=' vlist
if vlist="" then do
'HEADER ADD Accept-Ranges: bytes '
return 0 /* no range list found */
end
filen=chars(afile)
aa=stream(afile,'c','close')
/* if bad request, signal 0 to return entire file */
ndo=0
do until vlist=""
parse var vlist aterm ',' vlist
parse var aterm t1 '-' t2
if t1<>"" then
if datatype(t1)<>'NUM' then iterate
if t2<>"" then
if datatype(t2)<>'NUM' then return iterate
if t1="" & t2="" then iterate
if t2="" then t2=filen
if t1="" then do
t1=filen-t2
t2=filen
end
if t1<0 then t1=0
if t2>(filen-1) then t2=filen-1
if t2<t1 then iterate /* bad request */
ndo=ndo+1
r1.ndo=t1 ; r2.ndo=t2
end
if ndo=0 then do
'HEADER ADD Accept-Ranges: bytes '
say " no acceptable ranges "
return 0 /* no acceptable ranges */
end
atd=dosfileinfo(afile,'W')
dd1=word(atd,1) ; tt1=word(atd,2)
atd1=dateconv(dd1,'U','B')
parse var tt1 hr ':' min ':' sec
atd2=hr*3600 + min*60 + sec
atd3=sref_new_gmt(0,atd1,atd2)
if ndo>1 then say " Byte serving # ranges= " ndo
if ndo=1 then do
aa=sref_expire_response(-1,filen,atype,'Y')
'SET NETBUFFER OFF'
'RESPONSE HTTP/1.0 206 Partial Content' /* Set HTTP response line */
t1=r1.1 ; t2=r2.1
nget=(1+t2)-t1
outbig=charin(afile,1+t1,nget)
boog='VAR TYPE '|| atype|| ' as '|| afile ' NAME OUTBIG '
foo1=t1||'-'||t2'/'filen
'HEADER ADD Content-length:' foo1
'HEADER ADD Last-Modified: ' atd3 ' GMT '
'HEADER ADD Accept-Ranges: bytes '
return boog
end
/* else, multi part send */
/* check to see that it's supported */
conn1=translate(strip(reqfield('connection:')))
if conn1="MAINTAIN" | conn1="KEEP-ALIVE" then
nop
else do
'HEADER ADD Accept-Ranges: bytes '
say " connection:keep-alive not specified "
return 0
end
crlf='0d0a'x /* useful */
bound=copies("x",41) /* boundary data for part [could be random] */
mimestart='--'bound''crlf /* starts a MIME multipart section */
mimeend ='--'bound'--'crlf /* ends a MIME multipart section */
/* Send the header and first boundary */
'RESPONSE HTTP/1.0 206 Partial Content' /* Set HTTP response line */
'set netbuffer off' /* turn off buffering */
'send type multipart/x-byteranges;boundary='bound
'string' mimestart /* Or could be: 'var name mimestart' */
do mm=1 to ndo
outbig=''
outbig=outbig||'Content-type: '||atype||crlf
t1=r1.mm ; t2=r2.mm
nget=(1+t2)-t1
out1=charin(afile,1+t1,nget)
foo1=t1||'-'||t2'/'filen
outbig=outbig||'Content-range: bytes '|| foo1||crlf
outbig=outbig||crlf
outbig=outbig||out1||crlf
if mm< ndo then
outbig=outbig||mimestart
else
outbig=outbig||mimeend
'var name outbig '
end
'SEND COMPLETE '
return 'CLOSE'
/******************/
/* send file in 500byte chunks (get around netscape drop sockets? problem)*/
send_bits:procedure
parse arg afile
'SET NETBUFFER OFF'
'SEND TYPE text/html as ' afile
mm=chars(afile)
nn=1
do until nn>mm
oy=charin(afile,nn,1000)
say nn mm length(oy)
oy=translate(oy,' ','000a0d1a'x)
'VAR NAME OY '
nn=nn+1000
end
return ' '
say " pre send complete "
'SEND COMPLETE'
say " post send compte "
return ' '